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 +42 -89
- package/dist/cli.js +3 -3
- package/package.json +2 -8
- package/public/readme/architecture.svg +192 -0
- package/public/readme/home-workspace.png +0 -0
- package/public/readme/inbox-approvals.png +0 -0
- package/public/readme/workflow-blueprints.png +0 -0
- package/src/lib/notifications/actionable.ts +1 -1
- package/public/desktop-icon-512.png +0 -0
- package/src/lib/tauri-bridge.ts +0 -138
package/README.md
CHANGED
|
@@ -1,49 +1,38 @@
|
|
|
1
1
|
# Stagent
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
[](https://github.com/navam-io/stagent/releases/latest/download/Stagent.dmg) [](https://github.com/navam-io/stagent/releases/latest)
|
|
3
|
+
> Govern Your AI Agents. Operate With Oversight.
|
|
6
4
|
|
|
7
5
|
[](https://nextjs.org/) [](https://react.dev/) [](https://www.typescriptlang.org/) [](https://docs.anthropic.com/) [](https://developers.openai.com/codex/app-server) [](LICENSE)
|
|
8
6
|
|
|
9
|
-
##
|
|
7
|
+
## Quick Start
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
```bash
|
|
10
|
+
npx stagent
|
|
11
|
+
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Open [localhost:3000](http://localhost:3000).
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**Profiles & Policies** · **Blueprints & Schedules** · **Open Source**
|
|
16
16
|
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
---
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
## Why Stagent
|
|
24
26
|
|
|
25
|
-
|
|
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
|
-
|
|
29
|
+
---
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
## Runtime Bridge
|
|
32
32
|
|
|
33
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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://
|
|
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://
|
|
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
|
-
|
|
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 `
|
|
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
|
|
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
|
|
370
|
+
STAGENT_DATA_DIR=/tmp/stagent node dist/cli.js --reset
|
|
371
371
|
`;
|
|
372
|
-
program.name("stagent").description("Governed
|
|
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
|
-
"description": "Governed
|
|
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 & 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
|
|
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"
|
|
20
|
+
export type NotificationChannelId = "in_app" | "browser";
|
|
21
21
|
|
|
22
22
|
export interface ActionableNotificationPayload {
|
|
23
23
|
notificationId: string;
|
|
Binary file
|
package/src/lib/tauri-bridge.ts
DELETED
|
@@ -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
|
-
}
|