Files
clacky-ai--openclacky/docs/session-skill-invocation.md
wehub-resource-sync 9f2e1cd2af
Ruby / Ruby 4.0.5 (push) Failing after 5m13s
Ruby / Ruby 3.3.4 (push) Failing after 5m43s
Ruby / Ruby 2.6.8 (push) Failing after 16m2s
chore: import upstream snapshot with attribution
2026-07-13 12:33:07 +08:00

70 lines
2.4 KiB
Markdown

# Session + Skill Invocation Pattern
> Design pattern for launching an Agent session that immediately runs a skill.
> Follow this whenever a UI action needs to "open a session and do something automatically."
---
## The Pattern
```
1. POST /api/sessions → create a named session
2. Sessions.add(session) → register locally
3. Sessions.renderList() → update sidebar
4. _bootUI() if needed → connect WS (only on first boot)
5. Sessions.select(session.id) → navigate to session (triggers WS subscribe)
6. WS.send({ type: "message", session_id, content: "/skill-name" })
→ agent runs the skill immediately
```
The slash command (`/skill-name`) is handled by `Agent#parse_skill_command` on the
server side — no special API endpoint or pending-state machinery needed.
---
## Real Usages
### Create Task (`tasks.js → createInSession`)
```js
Sessions.select(session.id);
WS.send({ type: "message", session_id: session.id, content: "/create-task" });
```
### Onboard (`onboard.js → _startSoulSession`)
```js
_bootUI(); // WS.connect() + Tasks/Skills load
Sessions.add(session);
Sessions.renderList();
Sessions.select(session.id);
WS.send({ type: "message", session_id: session.id, content: "/onboard" });
```
---
## When to Use `pending_task` Instead
Use the `pending_task` registry field (and the `run_task` WS message) **only** when
the prompt is a large block of text read from a file (e.g. `POST /api/tasks/run`).
For slash commands, always prefer the direct `WS.send` approach above — simpler and
no server-side state to manage.
---
## Anti-patterns Avoided
| Anti-pattern | Why it was wrong |
|---|---|
| Store `_pendingSessionId` in module state, resolve on `session_list` | Race condition between WS connect and session_list arrival; unnecessary complexity |
| Custom `takePendingSession()` hook in app.js `session_list` handler | Spread logic across files; hard to trace |
| Send prompt via `setTimeout` after boot | Fragile timing; breaks if WS is slow |
---
## Key Insight
`Sessions.select(id)` triggers a WS `subscribe` message. Once the server confirms
with `subscribed`, the client is guaranteed to receive all subsequent broadcasts for
that session. Sending `WS.send({ type: "message" })` right after `select` is safe
because the WebSocket driver queues messages until the connection is open.