Changeset 0.28.6 (#192)

This commit is contained in:
2026-03-15 01:33:38 +00:00
parent 6f0ad1355c
commit bffda043db
59 changed files with 3022 additions and 77 deletions

View File

@@ -234,3 +234,15 @@ POST /admin/tasks/:id/run → force run
POST /admin/tasks/:id/kill → cancel active run
DELETE /admin/tasks/:id → delete task
```
### Notifications Admin (v0.28.6)
```
POST /admin/notifications/broadcast ← { "title", "message", "level?" }
→ { "message": "broadcast sent", "count": N }
```
Sends a `system.announcement` notification to all active users.
Also emits a `system.broadcast` WebSocket event for real-time delivery.
`level` is optional (`info` | `warning` | `critical`, default `info`).
See [notifications.md](notifications.md#admin-broadcast-v0286) for full details.

View File

@@ -91,6 +91,42 @@ POST /admin/notifications/test-email → send test email to requesting ad
Requires admin role. Loads SMTP config from platform settings,
sends a test message to the admin's registered email address.
### Admin Broadcast (v0.28.6)
```
POST /admin/notifications/broadcast
```
**Auth:** Admin only.
Sends a `system.announcement` notification to all active users via
`NotifyMany`. Also emits a `system.broadcast` WebSocket event for
real-time visibility.
**Request body:**
```json
{
"title": "Scheduled Maintenance",
"message": "Servers will be down from 2-4 AM EST.",
"level": "warning"
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | yes | Short summary shown in bell dropdown |
| `message` | string | yes | Detail text |
| `level` | string | no | `info` (default), `warning`, or `critical` |
**Response:**
```json
{ "message": "broadcast sent", "count": 42 }
```
`count` is the number of active users notified.
### WebSocket Events
| Event | Payload | Description |
@@ -98,6 +134,7 @@ sends a test message to the admin's registered email address.
| `notification.new` | Full notification object | New notification created |
| `notification.read` | `{"id": "uuid"}` | Single notification marked read |
| `notification.read` | `{"action": "mark_all_read"}` | All notifications marked read |
| `system.broadcast` | `{"title", "message", "level"}` | Admin announcement (v0.28.6) |
These are targeted via `Hub.SendToUser` — no room subscription
required. Used for badge sync across tabs.
@@ -106,7 +143,8 @@ required. Used for badge sync across tabs.
See [enums.md](enums.md#notification-types) for the canonical list.
Types are free-form strings (`domain.action` convention) — new types
don't require migration.
don't require migration. Core types include `system.announcement`
(admin broadcast, v0.28.6).
### Retention

View File

@@ -65,8 +65,17 @@ POST /tasks
```
`task_type`: `prompt` (direct LLM execution), `workflow` (deferred —
not yet implemented; creation is rejected at the API), or `action`
(no LLM — relay/webhook only; requires `task.action` permission).
not yet implemented; creation is rejected at the API), `action`
(no LLM — relay/webhook only; requires `task.action` permission),
or `system` (v0.28.6 — built-in Go function, admin-only).
System tasks require `system_function` — a registered function name
from the platform's Go function registry. No LLM, no provider
resolution, no channel needed. The function receives the store set
and returns a result string. Designed for core platform ops
(`session_cleanup`, `staleness_check`, `retention_sweep`, `health_prune`)
that must not break from bad user code. Permanent track — not replaced
by Starlark in v0.29.0.
`schedule`: cron expression, `once`, or `webhook`. Supported cron
patterns: `@hourly`, `@daily`, `@weekly`, `*/N * * * *` (every N
@@ -173,6 +182,26 @@ Returns `410 Gone` if the task is inactive.
**Rate limiting:** Governed by global request rate limits. No per-task
rate limiting in v0.28 — defer to a future version if needed.
### Task-to-Task Chaining (v0.28.6)
Tasks can trigger other tasks by setting the `webhook_url` of Task A
to the trigger URL of Task B:
1. Create **Task B** with `schedule: "webhook"`. Note the trigger URL
from the response (`/api/v1/hooks/t/{trigger_token}`).
2. Create **Task A** with `output_mode: "webhook"` and set `webhook_url`
to Task B's trigger URL.
3. When Task A completes, its output is POSTed to Task B's trigger URL.
Task B starts with Task A's output as its `trigger_payload`.
This enables multi-step pipelines without external orchestration:
- Task A: "Summarize today's news" (prompt, cron: 6am)
- Task B: "Format and post to Slack" (action, webhook-triggered)
The `webhook_secret` on Task A signs outbound payloads with HMAC-SHA256.
Task B's trigger endpoint does not currently verify signatures (the
trigger token itself is the auth mechanism).
## Team Tasks
Team-scoped tasks visible to all team members, manageable by team admins.
@@ -240,6 +269,30 @@ DELETE /admin/tasks/:id
**Auth:** Platform admin.
### System Functions (v0.28.6)
```
GET /admin/system-functions → { "data": [SystemFuncInfo, ...] }
```
Returns all registered built-in system function names and descriptions.
Used by the admin UI to populate the function dropdown when creating
system tasks.
**SystemFuncInfo:**
```json
{
"name": "session_cleanup",
"description": "Remove expired anonymous visitor sessions"
}
```
Built-in functions (v0.28.6): `session_cleanup`, `staleness_check`,
`retention_sweep`, `health_prune`.
**Auth:** Platform admin.
## Task Object
```json
@@ -250,7 +303,8 @@ DELETE /admin/tasks/:id
"name": "Morning News Digest",
"description": "...",
"scope": "personal|team|global",
"task_type": "prompt|workflow|action",
"task_type": "prompt|workflow|action|system",
"system_function": "session_cleanup",
"persona_id": "uuid|null",
"model_id": "claude-sonnet-4-20250514",
"system_prompt": "...",

View File

@@ -361,13 +361,15 @@ Independent of workspace scope — credentials are user-level.
```
POST /git-credentials ← { "name", "auth_type", ... }
POST /git-credentials/generate ← { "name", "persona_id?" }
GET /git-credentials → { "data": [GitCredentialSummary] }
GET /git-credentials/:id/public-key → { "public_key", "fingerprint" }
DELETE /git-credentials/:id
```
**Auth:** Authenticated user. Credentials are scoped to the requesting user.
**Create request:**
**Create request (manual):**
`auth_type` determines which fields are required:
@@ -379,6 +381,37 @@ DELETE /git-credentials/:id
Returns `201` with the credential summary (never exposes encrypted data).
**Generate request (v0.28.6 — server-side SSH keygen):**
```json
{
"name": "GitHub - main",
"persona_id": "uuid"
}
```
Generates an ED25519 SSH keypair server-side. The private key is
vault-encrypted before storage and never leaves the server. Returns
the credential summary including the public key (authorized_keys
format) and SHA256 fingerprint. The user adds the public key to their
git host (GitHub, Gitea, etc.).
Optional `persona_id` binds the key to a persona for commit
attribution — git operations using this credential will set the
commit author to the persona's name and handle.
Returns `201`.
**Get Public Key:**
```
GET /git-credentials/:id/public-key
```
Returns `{ "public_key": "ssh-ed25519 AAAA...", "fingerprint": "SHA256:..." }`.
Owner-only — returns `404` for other users or credentials without
a public key (e.g. PAT credentials).
**List response:**
```json
@@ -388,12 +421,18 @@ Returns `201` with the credential summary (never exposes encrypted data).
"id": "uuid",
"name": "GitHub PAT",
"auth_type": "https_pat",
"public_key": "",
"fingerprint": "",
"persona_id": null,
"created_at": "2025-06-15T14:30:00Z"
}
]
}
```
`public_key` and `fingerprint` are populated for `ssh_key` type
credentials created via `/generate`. Empty for PAT/basic auth.
**DELETE** returns `{"deleted": true}`. Returns `404` if credential
not found or not owned by the requesting user.

View File

@@ -30,9 +30,9 @@ v0.9.xv0.27.5 Foundation → Extensions → Surfaces → Auth ✅
├─ v0.28.5 Frontend SDK + Pipes ✅
│ (switchboard-sdk.js, pipe/filter
│ pipeline, component mounting)
├─ v0.28.6 Infrastructure
│ (virtual scroll, Helm, task webhook
UI, system tasks, model prefs)
├─ v0.28.6 Infrastructure
│ (virtual scroll, Helm, system tasks,
broadcast, git keygen, model prefs)
└─ v0.28.7 Unified Packaging + Task RBAC
(.pkg archive, manifest.type,
task permission gate pre-Starlark)
@@ -295,27 +295,24 @@ Pipeline wires filters in priority order; any filter can halt the chain.
- [x] Pipe filter tests: priority ordering, halt semantics, error isolation,
scoped/unscoped execution
### v0.28.6 — Infrastructure
- [ ] Virtual scroll for long conversations (prerequisite for heavy task output channels)
- [ ] KB auto-injection: platform-registered pre-send pipe filter (`_kb-auto-inject`,
priority 5), top-K chunk prepend to `ctx.metadata.kb_context`, context budget aware,
per-channel toggle. First real validation of the v0.28.5 pipeline architecture.
- [ ] Helm chart (replaces raw k8s manifests, `helm install switchboard ./chart`)
- [ ] Per-provider model preferences — finalize: make `provider_config_id` required on
### v0.28.6 — Infrastructure
- [x] Virtual scroll for long conversations (prerequisite for heavy task output channels)
- [x] Helm chart (replaces raw k8s manifests, `helm install switchboard ./chart`)
- [x] Per-provider model preferences — finalize: make `provider_config_id` required on
`PUT /models/preferences` (fixes NULL-in-UNIQUE dedup bug), migrate `/models/enabled`
and `/models/preferences` to `{"data": [...]}` envelope, add integration test coverage
- [ ] Git credentials settings UI: vault-encrypted per-user credentials, SSH key
management, per-workspace remote config. Table exists (`git_credentials`), vault
pattern exists — needs settings section and CRUD handler.
- [ ] `system.announcement` notification type: admin broadcast endpoint
- [x] Git credentials settings UI: server-side ED25519 key generation (private key
vault-encrypted, public key exposed for git host), optional persona binding for
commit attribution, settings section with generate/list/copy/delete
- [x] `system.announcement` notification type: admin broadcast endpoint
(`POST /admin/notifications/broadcast`), fan-out to all active users via
`NotifyMany`, admin UI for composing announcements
- [ ] Task webhook trigger UI: schedule selector gains `webhook` option,
- [x] Task webhook trigger UI: schedule selector gains `webhook` option,
trigger URL displayed + copy button, outbound `webhook_url` + `webhook_secret`
fields in create/edit form, task-to-task chaining documentation in admin UI.
Backend fully implemented — this is pure admin UI work.
- [ ] System task type: `task_type: "system"` — built-in Go function registry
(`retention_sweep`, `memory_compact`, `session_cleanup`, `staleness_check`).
- [x] System task type: `task_type: "system"` — built-in Go function registry
(`retention_sweep`, `health_prune`, `session_cleanup`, `staleness_check`).
Admin creates task, picks function from dropdown, sets cron schedule. Executor
calls registered Go function instead of LLM completion. Replaces the current
goroutine-based background jobs with visible, configurable, auditable tasks.
@@ -323,6 +320,9 @@ Pipeline wires filters in priority order; any filter can halt the chain.
**Permanent track** — Go registry is not replaced by Starlark (v0.29.0).
Core platform ops must not break from bad user code. Admin-only by design
(no RBAC needed — hardcoded to admin role).
- KB auto-injection moved to v0.29.0 — built as the reference Go implementation
of the server-side filter model alongside Starlark. Ensures the filter
architecture is right and performant before user code runs in it.
### v0.28.7 — Unified Packaging + Task RBAC
Single `.pkg` archive format for both surfaces and extensions. Manifest
@@ -453,6 +453,12 @@ extension infrastructure (v0.11.0).
data quality checks, integration sync, cleanup scripts. Editable,
versionable, sandbox-isolated, permission-gated.
- [ ] KB auto-injection: server-side pre-completion filter (Go built-in,
not Starlark). Top-K chunk prepend from channel-bound KBs, context
budget aware, per-channel toggle. Reference implementation of the
server-side filter model that Starlark filters will mirror. Moved
from v0.28.6 to validate alongside the Starlark filter architecture.
---
## v0.29.1 — API Extensions