jagc 0.1.0
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/.env.example +7 -0
- package/CHANGELOG.md +46 -0
- package/LICENSE +21 -0
- package/README.md +220 -0
- package/defaults/AGENTS.md +84 -0
- package/defaults/SYSTEM.md +27 -0
- package/defaults/settings.json +3 -0
- package/deploy/launchd/com.jagc.server.plist +58 -0
- package/deploy/systemd/jagc-restart.service +6 -0
- package/deploy/systemd/jagc.env.example +23 -0
- package/deploy/systemd/jagc.path +12 -0
- package/deploy/systemd/jagc.service +24 -0
- package/dist/api-contracts-BPwE7cri.mjs +118 -0
- package/dist/cli/main.mjs +1008 -0
- package/dist/server/main.mjs +3533 -0
- package/docs/architecture.md +161 -0
- package/docs/auth.md +104 -0
- package/docs/future.md +57 -0
- package/docs/release.md +81 -0
- package/docs/testing.md +58 -0
- package/migrations/001_runs_and_ingest.sql +24 -0
- package/migrations/002_thread_sessions.sql +9 -0
- package/package.json +86 -0
package/.env.example
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to jagc are documented here.
|
|
4
|
+
|
|
5
|
+
This project follows a readable, sectioned changelog format with a permanent **Unreleased** section.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- _None yet._
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- _None yet._
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- _None yet._
|
|
20
|
+
|
|
21
|
+
## [0.1.0] - 2026-02-09
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- npm-distributable CLI package (`jagc`) with built artifacts and curated package contents.
|
|
26
|
+
- First supported macOS deployment path via top-level CLI service commands:
|
|
27
|
+
- `jagc install`
|
|
28
|
+
- `jagc status`
|
|
29
|
+
- `jagc restart`
|
|
30
|
+
- `jagc uninstall`
|
|
31
|
+
- `jagc doctor`
|
|
32
|
+
- Launchd-managed per-user service installation with workspace-scoped logs and safety defaults.
|
|
33
|
+
- Package smoke loop (`pnpm test:pack`) validating tarball install + runnable server + CLI roundtrip.
|
|
34
|
+
- CI release gate workflow (`pnpm release:gate`) on GitHub Actions.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Deployment docs now treat macOS npm+launchd as the primary supported install path.
|
|
39
|
+
- Local release gate consolidated into a single canonical command.
|
|
40
|
+
- Project license changed from UNLICENSED to MIT (`LICENSE` added).
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- Service status/restart/doctor health targeting now resolves host/port from installed service config.
|
|
45
|
+
- Service command failures now return clean one-line CLI errors (no stack dumps).
|
|
46
|
+
- Service manager no longer selects TypeScript server entrypoints for launchd runtime.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anton Kuzmenko <hotk@hey.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# jagc
|
|
2
|
+
|
|
3
|
+
> **jagc** = **j**ust **a** **g**ood **c**lanker.
|
|
4
|
+
|
|
5
|
+
Self-hosted AI assistant to automate your life:
|
|
6
|
+
- **pi-coding-agent** for agent runtime behavior (sessions, context files, skills/prompts/extensions/themes/packages)
|
|
7
|
+
- **SQLite-backed run state + in-process scheduler** for durable run tracking and execution
|
|
8
|
+
|
|
9
|
+
This README is intentionally short and v0-focused.
|
|
10
|
+
|
|
11
|
+
For current implemented architecture, see **[`docs/architecture.md`](docs/architecture.md)**.
|
|
12
|
+
For deferred APIs, deployment notes, and post-v0 plans, see **[`docs/future.md`](docs/future.md)**.
|
|
13
|
+
For testing loops (including Telegram behavioral polling clone), see **[`docs/testing.md`](docs/testing.md)**.
|
|
14
|
+
For release/publish procedure, see **[`docs/release.md`](docs/release.md)**.
|
|
15
|
+
Release history lives in **[`CHANGELOG.md`](CHANGELOG.md)**.
|
|
16
|
+
|
|
17
|
+
## Status
|
|
18
|
+
|
|
19
|
+
- **Pre-alpha.** Expect breaking changes.
|
|
20
|
+
- **v0 scope is implemented** (server + CLI + threading semantics + Telegram polling controls). CI merge gating runs in GitHub Actions (`pnpm release:gate`).
|
|
21
|
+
- npm publish is tag-driven via GitHub Actions (`vX.Y.Z` tags only, `latest` channel).
|
|
22
|
+
- Core server endpoints are in place: `/healthz`, `/v1/messages`, `/v1/runs/:run_id`, auth catalog/login endpoints (`/v1/auth/providers`, `/v1/auth/providers/:provider/login`, `/v1/auth/logins/:attempt_id{,/input,/cancel}`), `/v1/models`, and thread runtime controls (`/v1/threads/:thread_key/{runtime,model,thinking,session}`).
|
|
23
|
+
- CLI supports the happy path plus runtime controls: `message`, `run wait`, `health`, `auth providers`, `auth login`, `new`, `model list/get/set`, `thinking get/set`, and local service lifecycle commands (`install`, `status`, `restart`, `uninstall`, `doctor`).
|
|
24
|
+
- Default executor runs through pi SDK sessions with SQLite-backed durable run tracking and in-process scheduling/recovery.
|
|
25
|
+
- Same-thread queued follow-ups/steers are accepted and run completion is attributed via pi session events (not prompt promise timing).
|
|
26
|
+
- Same-thread turn ordering (`followUp` / `steer`) is enforced by per-thread pi session controllers; run dispatch/recovery is in-process and single-server-process scoped in v0.
|
|
27
|
+
- Telegram polling adapter is implemented (personal chats), including button-based runtime controls via `/settings`, `/new`, `/model`, `/thinking`, and `/auth`.
|
|
28
|
+
- Server/runtime logs are unified on structured Pino JSON with component-scoped child loggers and request completion events.
|
|
29
|
+
- Telegram chat UX now streams live run progress in-chat (append-style progress edits + typing indicator + compact `>` tool / `~` thinking snippets) while preserving final run output delivery.
|
|
30
|
+
- If Telegram foreground wait times out, the adapter keeps watching in the background and posts the final result when the run completes.
|
|
31
|
+
- Model/thinking changes from Telegram button pickers return to the `/settings` panel with updated runtime state.
|
|
32
|
+
- Outdated Telegram inline callbacks auto-recover by replacing the menu with the latest `/settings` panel.
|
|
33
|
+
- Telegram callback payload limits are enforced; over-limit model/auth options are hidden with an in-chat notice.
|
|
34
|
+
- `JAGC_RUNNER=echo` is available for deterministic smoke tests.
|
|
35
|
+
- macOS single-user deployment is supported via npm global install + launchd (`jagc install` / `status` / `restart` / `uninstall`). Manual templates remain under `deploy/` as fallback examples.
|
|
36
|
+
|
|
37
|
+
## Locked v0 technology stack
|
|
38
|
+
|
|
39
|
+
Source of truth: **[`AGENTS.md`](AGENTS.md)**.
|
|
40
|
+
|
|
41
|
+
- Runtime: TypeScript (ESM) on Node.js 20 + pnpm
|
|
42
|
+
- Server/API: Fastify + Zod + Pino
|
|
43
|
+
- CLI: Commander
|
|
44
|
+
- Agent/runtime: pi-coding-agent
|
|
45
|
+
- Durable run state: SQLite (`runs`, `message_ingest`, `thread_sessions`) + in-process scheduler
|
|
46
|
+
- Telegram: grammY (polling first)
|
|
47
|
+
- Quality/tooling: Biome + Vitest
|
|
48
|
+
- Build: tsdown
|
|
49
|
+
|
|
50
|
+
## v0 scope (shipped)
|
|
51
|
+
|
|
52
|
+
Shipped in v0:
|
|
53
|
+
|
|
54
|
+
- Server:
|
|
55
|
+
- `GET /healthz`
|
|
56
|
+
- `POST /v1/messages` (ingest message, start durable run, return `run_id`)
|
|
57
|
+
- `GET /v1/runs/:run_id` (status/output)
|
|
58
|
+
- CLI:
|
|
59
|
+
- `jagc message "..." --json`
|
|
60
|
+
- `jagc run wait <run_id> --json`
|
|
61
|
+
- `jagc install|status|restart|uninstall|doctor` (macOS service lifecycle + diagnostics)
|
|
62
|
+
- Telegram:
|
|
63
|
+
- **Polling mode only** for v0
|
|
64
|
+
- Personal chats only
|
|
65
|
+
- One active run per Telegram thread (`thread_key = telegram:chat:<chat_id>`)
|
|
66
|
+
- Queued input behavior aligned with pi semantics (`steer` / `followUp`)
|
|
67
|
+
- `/new` resets the current Telegram thread's pi session; the next message creates a fresh session
|
|
68
|
+
|
|
69
|
+
### v0 acceptance behavior
|
|
70
|
+
|
|
71
|
+
- `jagc message "ping" --json` returns JSON including:
|
|
72
|
+
- `run_id`
|
|
73
|
+
- `status` (`succeeded|failed|running`)
|
|
74
|
+
- `output` (when succeeded)
|
|
75
|
+
- `error.message` (when failed)
|
|
76
|
+
- If two messages arrive for the same thread while a run is active:
|
|
77
|
+
- `steer` interrupts at the next tool boundary
|
|
78
|
+
- `followUp` waits for idle
|
|
79
|
+
- Different threads run concurrently.
|
|
80
|
+
|
|
81
|
+
## Contracts to stabilize early
|
|
82
|
+
|
|
83
|
+
We intentionally lock only these early:
|
|
84
|
+
|
|
85
|
+
1. **Workspace contract** (`JAGC_WORKSPACE_DIR`) and override rules
|
|
86
|
+
2. **Ingress envelope + idempotency semantics** (internal)
|
|
87
|
+
3. **Thread/run concurrency + queued message behavior** (`steer` / `followUp`)
|
|
88
|
+
|
|
89
|
+
Everything else remains flexible during pre-alpha.
|
|
90
|
+
|
|
91
|
+
## Minimal workspace contract
|
|
92
|
+
|
|
93
|
+
`JAGC_WORKSPACE_DIR` points to a trusted local repo.
|
|
94
|
+
|
|
95
|
+
Canonical layout (v0):
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
$JAGC_WORKSPACE_DIR/
|
|
99
|
+
AGENTS.md # auto-created on first startup (global user profile + assistant instructions)
|
|
100
|
+
SYSTEM.md # auto-created on first startup (global assistant behavior baseline)
|
|
101
|
+
APPEND_SYSTEM.md # optional
|
|
102
|
+
skills/ # auto-seeded with bundled skills on first startup (no overwrite)
|
|
103
|
+
prompts/ # optional
|
|
104
|
+
extensions/ # auto-seeded with bundled extensions on first startup (no overwrite)
|
|
105
|
+
themes/ # optional
|
|
106
|
+
tools/ # optional
|
|
107
|
+
jagc.json # optional
|
|
108
|
+
settings.json # auto-created on first startup (pi workspace settings + default packages)
|
|
109
|
+
.gitignore # auto-managed: .sessions/, auth.json, git/, jagc.sqlite*
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Configuration (v0 minimum)
|
|
113
|
+
|
|
114
|
+
| Variable | Required | Notes |
|
|
115
|
+
| --- | --- | --- |
|
|
116
|
+
| `JAGC_DATABASE_PATH` | No | SQLite DB file path (default `$JAGC_WORKSPACE_DIR/jagc.sqlite`; relative paths resolve under `JAGC_WORKSPACE_DIR`) |
|
|
117
|
+
| `JAGC_WORKSPACE_DIR` | No | Workspace + pi agent directory (default `~/.jagc`) |
|
|
118
|
+
| `JAGC_HOST` | No | Server bind host (default `127.0.0.1`) |
|
|
119
|
+
| `JAGC_PORT` | No | Server bind port (default `31415`) |
|
|
120
|
+
| `JAGC_API_URL` | No | CLI API target (default `http://127.0.0.1:31415`) |
|
|
121
|
+
| `JAGC_RUNNER` | No | `pi` (default) or `echo` for deterministic local/smoke runs |
|
|
122
|
+
| `JAGC_LOG_LEVEL` | No | `info` by default (`fatal|error|warn|info|debug|trace|silent`) |
|
|
123
|
+
| `JAGC_TELEGRAM_BOT_TOKEN` | No | Required only when Telegram adapter is enabled |
|
|
124
|
+
| `JAGC_WEBHOOK_BEARER_TOKEN` | No | Required when generic `POST /v1/webhooks/:source` ingress is enabled |
|
|
125
|
+
|
|
126
|
+
Auth setup and provider credential details: [`docs/auth.md`](docs/auth.md).
|
|
127
|
+
|
|
128
|
+
By default jagc uses `JAGC_WORKSPACE_DIR=~/.jagc` for workspace files and sets `JAGC_DATABASE_PATH` to `$JAGC_WORKSPACE_DIR/jagc.sqlite`. On startup it ensures the directory exists, creates `SYSTEM.md`, `AGENTS.md`, and `settings.json` from built-in templates if missing (without overwriting existing files), seeds bundled `defaults/skills/**` and `defaults/extensions/**` files into the workspace if missing (without overwriting existing files), and keeps `.gitignore` entries for `.sessions/`, `auth.json`, `git/`, `jagc.sqlite`, `jagc.sqlite-shm`, and `jagc.sqlite-wal`. The default `settings.json` pre-installs `git:github.com/default-anton/pi-librarian` and `git:github.com/default-anton/pi-subdir-context`; users can edit/remove them later. jagc still does not copy `~/.pi/agent/{settings.json,auth.json}` automatically.
|
|
129
|
+
|
|
130
|
+
## Quick start (macOS, supported path)
|
|
131
|
+
|
|
132
|
+
1. Install Node.js 20+ and npm.
|
|
133
|
+
2. Install jagc globally:
|
|
134
|
+
- `npm install -g jagc@latest`
|
|
135
|
+
3. Install + start the user service:
|
|
136
|
+
- `jagc install`
|
|
137
|
+
4. Verify:
|
|
138
|
+
- `jagc status`
|
|
139
|
+
- `jagc health --json`
|
|
140
|
+
|
|
141
|
+
Update to latest:
|
|
142
|
+
|
|
143
|
+
- `npm install -g jagc@latest`
|
|
144
|
+
- `jagc restart`
|
|
145
|
+
|
|
146
|
+
Uninstall service (keep workspace data):
|
|
147
|
+
|
|
148
|
+
- `jagc uninstall`
|
|
149
|
+
|
|
150
|
+
Uninstall service and delete local workspace data (`~/.jagc` by default):
|
|
151
|
+
|
|
152
|
+
- `jagc uninstall --purge-data`
|
|
153
|
+
|
|
154
|
+
Remove the npm package binary as well:
|
|
155
|
+
|
|
156
|
+
- `npm uninstall -g jagc`
|
|
157
|
+
|
|
158
|
+
If the service fails to start, run diagnostics:
|
|
159
|
+
|
|
160
|
+
- `jagc doctor`
|
|
161
|
+
|
|
162
|
+
## Quick start (dev)
|
|
163
|
+
|
|
164
|
+
1. `mise install` (rerun when required tools are missing or `.tool-versions` changes)
|
|
165
|
+
2. `pnpm install`
|
|
166
|
+
3. Set required env vars (see `.env.example`)
|
|
167
|
+
4. `pnpm dev` (applies SQL migrations from `migrations/` on startup)
|
|
168
|
+
5. Verify:
|
|
169
|
+
- `pnpm smoke`
|
|
170
|
+
- `pnpm test`
|
|
171
|
+
- or manually: `pnpm dev:cli health --json` then `pnpm dev:cli message "ping" --json`
|
|
172
|
+
- inspect provider/model catalog: `pnpm dev:cli model list --json`
|
|
173
|
+
- inspect auth status / start OAuth login: `pnpm dev:cli auth providers --json` and `pnpm dev:cli auth login openai-codex`
|
|
174
|
+
- inspect thread runtime controls: `pnpm dev:cli model get --thread-key cli:default --json` and `pnpm dev:cli thinking get --thread-key cli:default --json`
|
|
175
|
+
- reset thread session: `pnpm dev:cli new --thread-key cli:default --json`
|
|
176
|
+
|
|
177
|
+
### CLI runtime controls (v0)
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# provider/model catalog
|
|
181
|
+
jagc model list --json
|
|
182
|
+
|
|
183
|
+
# read current thread model + thinking
|
|
184
|
+
jagc model get --thread-key cli:default --json
|
|
185
|
+
jagc thinking get --thread-key cli:default --json
|
|
186
|
+
|
|
187
|
+
# reset current thread session (next message starts fresh)
|
|
188
|
+
jagc new --thread-key cli:default --json
|
|
189
|
+
|
|
190
|
+
# set model + thinking for a thread
|
|
191
|
+
jagc model set openai/gpt-5 --thread-key cli:default --json
|
|
192
|
+
jagc thinking set medium --thread-key cli:default --json
|
|
193
|
+
|
|
194
|
+
# OAuth login via jagc broker
|
|
195
|
+
jagc auth providers --json
|
|
196
|
+
jagc auth login openai-codex
|
|
197
|
+
# optional: stable owner key to resume the same login flow
|
|
198
|
+
jagc auth login openai-codex --owner-key cli:default
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Security baseline
|
|
202
|
+
|
|
203
|
+
- Workspace code is **trusted** and runs with server permissions.
|
|
204
|
+
- Third-party pi packages are **trusted code**; review before installing.
|
|
205
|
+
- Local CLI usage requires no auth for v0.
|
|
206
|
+
- Generic webhook ingress (`POST /v1/webhooks/:source`) requires bearer-token auth (`Authorization: Bearer ...`).
|
|
207
|
+
- Hardening path after v0: add HMAC-signed payload verification + replay protection (timestamp/nonce window).
|
|
208
|
+
- Run as unprivileged user; keep secrets out of repos.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT. See [`LICENSE`](./LICENSE).
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Deferred scope
|
|
217
|
+
|
|
218
|
+
See **[`docs/future.md`](docs/future.md)** for the post-v0 roadmap (webhook hardening, CI automation, operator UX, and deployment maturity).
|
|
219
|
+
|
|
220
|
+
Pre-v0 long-form draft details were intentionally removed during docs tightening; recover them from git history if needed.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
## Global instructions (always on)
|
|
2
|
+
|
|
3
|
+
- Treat this file as the default instruction layer for all chats and projects.
|
|
4
|
+
- Be direct, opinionated, and practical. Skip corporate tone and filler.
|
|
5
|
+
- Never open with canned fluff ("Great question", "Absolutely", etc.).
|
|
6
|
+
- Keep answers short by default; expand only when asked or when risk is high.
|
|
7
|
+
- Call out bad ideas early. Be respectful, but don't sugarcoat.
|
|
8
|
+
- Ask clarifying questions only when blocked.
|
|
9
|
+
- When repository-specific instructions exist, follow the closest repo `AGENTS.md` for local rules and use this file for personal/global preferences.
|
|
10
|
+
|
|
11
|
+
## How this template works
|
|
12
|
+
|
|
13
|
+
- Each section has guidance plus placeholders.
|
|
14
|
+
- Guidance explains what belongs in the section.
|
|
15
|
+
- Placeholders are the parts to fill in and update over time.
|
|
16
|
+
- If something is unknown, leave `<fill me>` and continue.
|
|
17
|
+
|
|
18
|
+
## How to address me (the user)
|
|
19
|
+
|
|
20
|
+
### Guidance
|
|
21
|
+
|
|
22
|
+
- Use my preferred name/call-sign when known; otherwise skip name-based greetings.
|
|
23
|
+
- Mirror my language.
|
|
24
|
+
- Use pronouns only when explicitly known.
|
|
25
|
+
- Use timezone-aware answers when scheduling matters.
|
|
26
|
+
|
|
27
|
+
### Data
|
|
28
|
+
|
|
29
|
+
- Name: <fill me>
|
|
30
|
+
- Call me: <fill me>
|
|
31
|
+
- Pronouns: <fill me>
|
|
32
|
+
- Timezone: <fill me>
|
|
33
|
+
- Language(s): <fill me>
|
|
34
|
+
|
|
35
|
+
## User preferences (free-form)
|
|
36
|
+
|
|
37
|
+
### Guidance
|
|
38
|
+
|
|
39
|
+
- Store durable preferences here as they are learned in conversation.
|
|
40
|
+
- Keep entries concrete and actionable (tone, format, tooling, workflow, constraints).
|
|
41
|
+
- Prefer short bullets over prose.
|
|
42
|
+
|
|
43
|
+
### Entries
|
|
44
|
+
|
|
45
|
+
- <fill me>
|
|
46
|
+
- <fill me>
|
|
47
|
+
|
|
48
|
+
## Environment facts (agent-maintained)
|
|
49
|
+
|
|
50
|
+
### Guidance
|
|
51
|
+
|
|
52
|
+
- Discover and maintain these from the local environment; ask only when blocked.
|
|
53
|
+
- Keep values factual and specific.
|
|
54
|
+
|
|
55
|
+
### Data
|
|
56
|
+
|
|
57
|
+
- OS + shell: <fill me>
|
|
58
|
+
- Toolchains/package managers: <fill me>
|
|
59
|
+
- Core local services/devices: <fill me>
|
|
60
|
+
|
|
61
|
+
## Important paths (path + one-line purpose)
|
|
62
|
+
|
|
63
|
+
### Guidance
|
|
64
|
+
|
|
65
|
+
- Track high-signal paths as they become relevant.
|
|
66
|
+
- Format each entry as: ``/absolute/path`` — one sentence on what it contains and why it matters.
|
|
67
|
+
|
|
68
|
+
### Entries
|
|
69
|
+
|
|
70
|
+
- `/absolute/path` — <fill me>
|
|
71
|
+
- `/absolute/path` — <fill me>
|
|
72
|
+
|
|
73
|
+
## Safety and guardrails
|
|
74
|
+
|
|
75
|
+
- Confirm before destructive/irreversible actions.
|
|
76
|
+
- Never perform purchases, financial transfers, or account-level changes without explicit approval.
|
|
77
|
+
- Never expose secrets or private data outside the current task scope.
|
|
78
|
+
- If a request is risky or ambiguous, propose the safest concrete next step first.
|
|
79
|
+
|
|
80
|
+
## Maintenance
|
|
81
|
+
|
|
82
|
+
- Keep this file concise and instruction-first.
|
|
83
|
+
- Update it whenever the user corrects a preference.
|
|
84
|
+
- Update environment facts and important paths when newly discovered.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## Role
|
|
2
|
+
|
|
3
|
+
You are my personal AI assistant. Be useful, decisive, and honest.
|
|
4
|
+
|
|
5
|
+
## Core behavior
|
|
6
|
+
|
|
7
|
+
- Have a point of view. If there are multiple options, pick one by default and explain tradeoffs briefly.
|
|
8
|
+
- Don't sound corporate. Remove policy-speak, hedging, and fake enthusiasm.
|
|
9
|
+
- Never open with 'Great question', 'I'd be happy to help', or 'Absolutely'. Just answer.
|
|
10
|
+
- Brevity is mandatory. If one sentence works, use one sentence.
|
|
11
|
+
- Humor is allowed when it helps clarity or tone. Don't force it.
|
|
12
|
+
- Call out bad ideas early. Be blunt but respectful.
|
|
13
|
+
- Swearing is allowed when it lands. Keep it occasional and intentional.
|
|
14
|
+
- Don't pretend certainty: if unsure, say what you know, what you're assuming, and what to check next.
|
|
15
|
+
|
|
16
|
+
## Vibe
|
|
17
|
+
|
|
18
|
+
- Practical over performative.
|
|
19
|
+
- Warm, sharp, and unafraid to disagree.
|
|
20
|
+
- Treat me like a capable adult with limited time.
|
|
21
|
+
'Be the assistant you'd actually want to talk to at 2am. Not a corporate drone. Not a sycophant. Just... good.
|
|
22
|
+
|
|
23
|
+
## Safety defaults
|
|
24
|
+
|
|
25
|
+
- Confirm before destructive actions, purchases, account changes, or irreversible operations.
|
|
26
|
+
- Protect private data and secrets by default.
|
|
27
|
+
- If a request is risky or ambiguous, propose the safest concrete next step.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>Label</key>
|
|
6
|
+
<string>com.jagc.server</string>
|
|
7
|
+
|
|
8
|
+
<!--
|
|
9
|
+
Prefer `jagc install` for managed setup.
|
|
10
|
+
This file is the manual fallback when operators need to hand-edit launchd config.
|
|
11
|
+
-->
|
|
12
|
+
<key>ProgramArguments</key>
|
|
13
|
+
<array>
|
|
14
|
+
<string>/opt/homebrew/bin/node</string>
|
|
15
|
+
<string>/usr/local/lib/node_modules/jagc/dist/server/main.mjs</string>
|
|
16
|
+
</array>
|
|
17
|
+
|
|
18
|
+
<key>WorkingDirectory</key>
|
|
19
|
+
<string>/Users/you/.jagc</string>
|
|
20
|
+
|
|
21
|
+
<key>RunAtLoad</key>
|
|
22
|
+
<true/>
|
|
23
|
+
|
|
24
|
+
<key>KeepAlive</key>
|
|
25
|
+
<true/>
|
|
26
|
+
|
|
27
|
+
<key>ProcessType</key>
|
|
28
|
+
<string>Background</string>
|
|
29
|
+
|
|
30
|
+
<key>EnvironmentVariables</key>
|
|
31
|
+
<dict>
|
|
32
|
+
<key>PATH</key>
|
|
33
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
34
|
+
<key>JAGC_WORKSPACE_DIR</key>
|
|
35
|
+
<string>/Users/you/.jagc</string>
|
|
36
|
+
<key>JAGC_DATABASE_PATH</key>
|
|
37
|
+
<string>/Users/you/.jagc/jagc.sqlite</string>
|
|
38
|
+
<key>JAGC_HOST</key>
|
|
39
|
+
<string>127.0.0.1</string>
|
|
40
|
+
<key>JAGC_PORT</key>
|
|
41
|
+
<string>31415</string>
|
|
42
|
+
<key>JAGC_RUNNER</key>
|
|
43
|
+
<string>pi</string>
|
|
44
|
+
<key>JAGC_LOG_LEVEL</key>
|
|
45
|
+
<string>info</string>
|
|
46
|
+
<!-- Optional provider env vars -->
|
|
47
|
+
<!-- <key>OPENAI_API_KEY</key><string>...</string> -->
|
|
48
|
+
<!-- Optional Telegram polling -->
|
|
49
|
+
<!-- <key>JAGC_TELEGRAM_BOT_TOKEN</key><string>...</string> -->
|
|
50
|
+
</dict>
|
|
51
|
+
|
|
52
|
+
<key>StandardOutPath</key>
|
|
53
|
+
<string>/Users/you/.jagc/logs/server.out.log</string>
|
|
54
|
+
|
|
55
|
+
<key>StandardErrorPath</key>
|
|
56
|
+
<string>/Users/you/.jagc/logs/server.err.log</string>
|
|
57
|
+
</dict>
|
|
58
|
+
</plist>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Copy to: /etc/jagc/jagc.env
|
|
2
|
+
# Make sure this file is readable by the jagc service user.
|
|
3
|
+
|
|
4
|
+
# All jagc-specific env vars are prefixed with JAGC_.
|
|
5
|
+
# (Exception: PI_CODING_AGENT_DIR is owned by pi-coding-agent.)
|
|
6
|
+
|
|
7
|
+
# SQLite database file
|
|
8
|
+
JAGC_DATABASE_PATH=/var/lib/jagc/workspace/jagc.sqlite
|
|
9
|
+
|
|
10
|
+
# Path to the user config repo (workflows/, AGENTS.md, SYSTEM.md, skills/, ... )
|
|
11
|
+
JAGC_WORKSPACE_DIR=/var/lib/jagc/workspace
|
|
12
|
+
|
|
13
|
+
# Server bind
|
|
14
|
+
JAGC_PORT=31415
|
|
15
|
+
|
|
16
|
+
# Optional: isolate pi-coding-agent state (sessions, global settings)
|
|
17
|
+
# PI_CODING_AGENT_DIR=/var/lib/jagc/pi-agent
|
|
18
|
+
|
|
19
|
+
# Provider credentials (example)
|
|
20
|
+
# OPENAI_API_KEY=...
|
|
21
|
+
|
|
22
|
+
# Telegram (optional)
|
|
23
|
+
# JAGC_TELEGRAM_BOT_TOKEN=...
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=EXPERIMENTAL: restart jagc when workspace workflows change
|
|
3
|
+
|
|
4
|
+
# Safer default: do NOT enable this unit in production.
|
|
5
|
+
# Prefer explicit/manual restarts after reviewing workspace changes.
|
|
6
|
+
# If you opt in, scope restarts to workflow code changes only.
|
|
7
|
+
[Path]
|
|
8
|
+
PathModified=/var/lib/jagc/workspace/workflows
|
|
9
|
+
Unit=jagc-restart.service
|
|
10
|
+
|
|
11
|
+
[Install]
|
|
12
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=jagc server
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=simple
|
|
8
|
+
User=jagc
|
|
9
|
+
Group=jagc
|
|
10
|
+
WorkingDirectory=/var/lib/jagc/workspace
|
|
11
|
+
EnvironmentFile=/etc/jagc/jagc.env
|
|
12
|
+
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
13
|
+
ExecStart=/usr/bin/env node /usr/local/lib/node_modules/jagc/dist/server/main.mjs
|
|
14
|
+
ExecReload=/bin/kill -HUP $MAINPID
|
|
15
|
+
Restart=on-failure
|
|
16
|
+
RestartSec=2
|
|
17
|
+
TimeoutStopSec=30
|
|
18
|
+
KillSignal=SIGINT
|
|
19
|
+
|
|
20
|
+
# Logs end up in journald. View with:
|
|
21
|
+
# journalctl -u jagc -f
|
|
22
|
+
|
|
23
|
+
[Install]
|
|
24
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/run-types.ts
|
|
4
|
+
const deliveryModes = ["steer", "followUp"];
|
|
5
|
+
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/shared/api-contracts.ts
|
|
8
|
+
const deliveryModeSchema = z.enum(deliveryModes);
|
|
9
|
+
const thinkingLevels = [
|
|
10
|
+
"off",
|
|
11
|
+
"minimal",
|
|
12
|
+
"low",
|
|
13
|
+
"medium",
|
|
14
|
+
"high",
|
|
15
|
+
"xhigh"
|
|
16
|
+
];
|
|
17
|
+
const thinkingLevelSchema = z.enum(thinkingLevels);
|
|
18
|
+
const postMessageRequestSchema = z.object({
|
|
19
|
+
source: z.string().trim().min(1).default("cli"),
|
|
20
|
+
thread_key: z.string().trim().min(1).default("cli:default"),
|
|
21
|
+
user_key: z.string().trim().min(1).optional(),
|
|
22
|
+
text: z.string().min(1),
|
|
23
|
+
delivery_mode: deliveryModeSchema.default("followUp"),
|
|
24
|
+
idempotency_key: z.string().trim().min(1).optional()
|
|
25
|
+
});
|
|
26
|
+
const runParamsSchema = z.object({ run_id: z.string().trim().min(1) });
|
|
27
|
+
const threadParamsSchema = z.object({ thread_key: z.string().trim().min(1) });
|
|
28
|
+
const authProviderParamsSchema = z.object({ provider: z.string().trim().min(1) });
|
|
29
|
+
const authLoginAttemptParamsSchema = z.object({ attempt_id: z.string().trim().min(1) });
|
|
30
|
+
const oauthOwnerHeaderName = "x-jagc-auth-owner";
|
|
31
|
+
const runResponseSchema = z.object({
|
|
32
|
+
run_id: z.string(),
|
|
33
|
+
status: z.enum([
|
|
34
|
+
"running",
|
|
35
|
+
"succeeded",
|
|
36
|
+
"failed"
|
|
37
|
+
]),
|
|
38
|
+
output: z.record(z.string(), z.unknown()).nullable(),
|
|
39
|
+
error: z.object({ message: z.string() }).nullable()
|
|
40
|
+
});
|
|
41
|
+
const apiErrorResponseSchema = z.object({ error: z.object({
|
|
42
|
+
code: z.string(),
|
|
43
|
+
message: z.string()
|
|
44
|
+
}) });
|
|
45
|
+
const providerModelSchema = z.object({
|
|
46
|
+
provider: z.string(),
|
|
47
|
+
model_id: z.string(),
|
|
48
|
+
name: z.string(),
|
|
49
|
+
reasoning: z.boolean(),
|
|
50
|
+
available: z.boolean()
|
|
51
|
+
});
|
|
52
|
+
const providerAuthStatusSchema = z.object({
|
|
53
|
+
provider: z.string(),
|
|
54
|
+
has_auth: z.boolean(),
|
|
55
|
+
credential_type: z.enum(["api_key", "oauth"]).nullable(),
|
|
56
|
+
oauth_supported: z.boolean(),
|
|
57
|
+
env_var_hint: z.string().nullable(),
|
|
58
|
+
total_models: z.number().int().nonnegative(),
|
|
59
|
+
available_models: z.number().int().nonnegative()
|
|
60
|
+
});
|
|
61
|
+
const providerCatalogSchema = providerAuthStatusSchema.extend({ models: z.array(providerModelSchema) });
|
|
62
|
+
const authProvidersResponseSchema = z.object({ providers: z.array(providerAuthStatusSchema) });
|
|
63
|
+
const modelCatalogResponseSchema = z.object({ providers: z.array(providerCatalogSchema) });
|
|
64
|
+
const oauthLoginPromptKindSchema = z.enum(["prompt", "manual_code"]);
|
|
65
|
+
const oauthLoginPromptSchema = z.object({
|
|
66
|
+
kind: oauthLoginPromptKindSchema,
|
|
67
|
+
message: z.string(),
|
|
68
|
+
placeholder: z.string().nullable(),
|
|
69
|
+
allow_empty: z.boolean()
|
|
70
|
+
});
|
|
71
|
+
const oauthLoginAttemptStatusSchema = z.enum([
|
|
72
|
+
"running",
|
|
73
|
+
"awaiting_input",
|
|
74
|
+
"succeeded",
|
|
75
|
+
"failed",
|
|
76
|
+
"cancelled"
|
|
77
|
+
]);
|
|
78
|
+
const oauthLoginAttemptSchema = z.object({
|
|
79
|
+
attempt_id: z.string(),
|
|
80
|
+
owner_key: z.string(),
|
|
81
|
+
provider: z.string(),
|
|
82
|
+
provider_name: z.string().nullable(),
|
|
83
|
+
status: oauthLoginAttemptStatusSchema,
|
|
84
|
+
auth: z.object({
|
|
85
|
+
url: z.string(),
|
|
86
|
+
instructions: z.string().nullable()
|
|
87
|
+
}).nullable(),
|
|
88
|
+
prompt: oauthLoginPromptSchema.nullable(),
|
|
89
|
+
progress_messages: z.array(z.string()),
|
|
90
|
+
error: z.string().nullable()
|
|
91
|
+
});
|
|
92
|
+
const submitOAuthLoginInputRequestSchema = z.object({
|
|
93
|
+
kind: oauthLoginPromptKindSchema.optional(),
|
|
94
|
+
value: z.string()
|
|
95
|
+
});
|
|
96
|
+
const threadRuntimeStateSchema = z.object({
|
|
97
|
+
thread_key: z.string(),
|
|
98
|
+
model: z.object({
|
|
99
|
+
provider: z.string(),
|
|
100
|
+
model_id: z.string(),
|
|
101
|
+
name: z.string().nullable()
|
|
102
|
+
}).nullable(),
|
|
103
|
+
thinking_level: thinkingLevelSchema,
|
|
104
|
+
supports_thinking: z.boolean(),
|
|
105
|
+
available_thinking_levels: z.array(thinkingLevelSchema)
|
|
106
|
+
});
|
|
107
|
+
const setThreadModelRequestSchema = z.object({
|
|
108
|
+
provider: z.string().trim().min(1),
|
|
109
|
+
model_id: z.string().trim().min(1)
|
|
110
|
+
});
|
|
111
|
+
const setThreadThinkingRequestSchema = z.object({ thinking_level: thinkingLevelSchema });
|
|
112
|
+
const resetThreadSessionResponseSchema = z.object({
|
|
113
|
+
thread_key: z.string(),
|
|
114
|
+
reset: z.literal(true)
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
export { oauthLoginAttemptSchema as a, resetThreadSessionResponseSchema as c, setThreadModelRequestSchema as d, setThreadThinkingRequestSchema as f, threadRuntimeStateSchema as g, threadParamsSchema as h, modelCatalogResponseSchema as i, runParamsSchema as l, thinkingLevels as m, authProviderParamsSchema as n, oauthOwnerHeaderName as o, submitOAuthLoginInputRequestSchema as p, authProvidersResponseSchema as r, postMessageRequestSchema as s, authLoginAttemptParamsSchema as t, runResponseSchema as u };
|