openclaw-code-agent 2.0.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/LICENSE +21 -0
- package/README.md +375 -0
- package/dist/index.js +65 -0
- package/openclaw.plugin.json +132 -0
- package/package.json +66 -0
- package/skills/code-agent-orchestration/SKILL.md +319 -0
- package/workflows/plan-approval.lobster +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 OpenClaw Contributors
|
|
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,375 @@
|
|
|
1
|
+
# OpenClaw Code Agent
|
|
2
|
+
|
|
3
|
+
An [OpenClaw](https://openclaw.com) plugin that lets AI agents orchestrate coding agent sessions as managed background processes. Launch, monitor, and interact with multiple concurrent coding sessions directly from Telegram, Discord, or any OpenClaw-supported messaging platform — without leaving your chat interface.
|
|
4
|
+
|
|
5
|
+
## Supported Agents
|
|
6
|
+
|
|
7
|
+
| Agent | Status | Notes |
|
|
8
|
+
|-------|--------|-------|
|
|
9
|
+
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | ✅ Supported | Full support via `@anthropic-ai/claude-agent-sdk` |
|
|
10
|
+
| [Codex](https://github.com/openai/codex) | ✅ Supported | Full support via `@openai/codex-sdk` thread API |
|
|
11
|
+
| Other agents | 🚧 Planned | Plugin architecture supports adding new harnesses |
|
|
12
|
+
|
|
13
|
+
> **vs. built-in ACP?** See [docs/ACP-COMPARISON.md](docs/ACP-COMPARISON.md) for a full breakdown.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- **Multi-session management** — Run multiple concurrent coding agent sessions, each with a unique ID and human-readable name
|
|
20
|
+
- **Plan → Execute workflow** — Claude Code sessions expose plan mode; Codex uses a soft first-turn planning prompt while staying externally in implement mode
|
|
21
|
+
- **Thread-based routing** — Notifications go to the Telegram thread/topic where the session was launched
|
|
22
|
+
- **Pause + auto-resume** — Non-question turn completion pauses sessions (`done`) and next `agent_respond` auto-resumes with context intact
|
|
23
|
+
- **Turn-end wake signaling** — Every turn end emits a deterministic wake signal with output preview and waiting hint
|
|
24
|
+
- **Smart waiting detection** — Heuristic waiting detector reduces false-positive wake escalations
|
|
25
|
+
- **Multi-turn conversations** — Send follow-up messages, interrupt, or iterate with a running agent
|
|
26
|
+
- **Session resume & fork** — Resume any completed session or fork it into a new conversation branch
|
|
27
|
+
- **Merged session listing** — `agent_sessions` shows active + persisted sessions in one view (deduped by internal session ID)
|
|
28
|
+
- **Pending MessageStream safety** — queued follow-ups are preserved across turn completion so messages are not dropped
|
|
29
|
+
- **Codex SDK streaming harness** — uses `@openai/codex-sdk` thread streaming with soft first-turn planning, waiting detection, and activity heartbeats
|
|
30
|
+
- **Multi-agent support** — Route notifications to the correct agent/chat via workspace-based channel mapping
|
|
31
|
+
- **Auto-respond rules** — Orchestrator auto-handles permission requests and confirmations; forwards real decisions to you
|
|
32
|
+
- **Anti-cascade protection** — Orchestrator never launches new sessions from wake events
|
|
33
|
+
- **Automatic cleanup** — Completed sessions are garbage-collected after a configurable TTL (`sessionGcAgeMinutes`, default 24h); IDs persist for resume
|
|
34
|
+
- **Harness-agnostic architecture** — Pluggable `AgentHarness` interface allows adding new coding agent backends
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### 1. Install the plugin
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
openclaw plugins install openclaw-code-agent
|
|
44
|
+
openclaw gateway restart
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Configure notifications
|
|
48
|
+
|
|
49
|
+
Add to `~/.openclaw/openclaw.json` under `plugins.entries["openclaw-code-agent"]`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"plugins": {
|
|
54
|
+
"entries": {
|
|
55
|
+
"openclaw-code-agent": {
|
|
56
|
+
"enabled": true,
|
|
57
|
+
"config": {
|
|
58
|
+
"fallbackChannel": "telegram|my-bot|123456789",
|
|
59
|
+
"maxSessions": 5
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Replace `my-bot` with your Telegram bot account name and `123456789` with your Telegram chat ID.
|
|
68
|
+
|
|
69
|
+
### 3. Typical workflow
|
|
70
|
+
|
|
71
|
+
1. Ask your agent: *"Fix the bug in auth.ts"*
|
|
72
|
+
2. A coding agent session launches and explores the task. Claude Code exposes **plan mode**; Codex can do a plan-first turn without surfacing plan mode in session status
|
|
73
|
+
3. The agent's questions and plan appear in the **same Telegram thread** where you launched
|
|
74
|
+
4. When a session is awaiting plan approval, approve it with `agent_respond(..., approve=true)` and the session switches to implement mode
|
|
75
|
+
5. The agent implements with full permissions, then you get a brief completion summary
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Tools
|
|
80
|
+
|
|
81
|
+
| Tool | Description | Key Parameters |
|
|
82
|
+
|------|-------------|----------------|
|
|
83
|
+
| `agent_launch` | Start a new coding agent session in background | `prompt`, `name`, `workdir`, `model`, `resume_session_id`, `fork_session`, `permission_mode`, `harness`, `notify_on_turn_end` |
|
|
84
|
+
| `agent_respond` | Send a follow-up message to a running session | `session`, `message`, `interrupt`, `approve`, `userInitiated` |
|
|
85
|
+
| `agent_kill` | Terminate or complete a running session | `session`, `reason` |
|
|
86
|
+
| `agent_output` | Read buffered output from a session | `session`, `lines`, `full` |
|
|
87
|
+
| `agent_sessions` | List recent sessions (5 by default, `full` for 24h view) | `status`, `full` |
|
|
88
|
+
| `agent_stats` | Show usage metrics (counts, durations, costs) | *(none)* |
|
|
89
|
+
|
|
90
|
+
Core orchestration workflows use `agent_launch`, `agent_respond`, `agent_output`, `agent_sessions`, and `agent_kill`.
|
|
91
|
+
|
|
92
|
+
All tools are also available as **chat commands** (`/agent`, `/agent_respond`, `/agent_kill`, `/agent_sessions`, `/agent_resume`, `/agent_stats`, `/agent_output`).
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Usage Examples
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Launch a session (starts in plan mode by default)
|
|
100
|
+
/agent Fix the authentication bug in src/auth.ts
|
|
101
|
+
/agent --name fix-auth Fix the authentication bug
|
|
102
|
+
|
|
103
|
+
# Monitor
|
|
104
|
+
/agent_sessions
|
|
105
|
+
/agent_sessions --full
|
|
106
|
+
|
|
107
|
+
# Interact with a running session
|
|
108
|
+
/agent_respond fix-auth Also add unit tests
|
|
109
|
+
/agent_respond --interrupt fix-auth Stop that and do this instead
|
|
110
|
+
|
|
111
|
+
# Approve a pending plan (tool call)
|
|
112
|
+
agent_respond(session='fix-auth', message='Approved. Go ahead.', approve=true)
|
|
113
|
+
|
|
114
|
+
# Lifecycle management
|
|
115
|
+
/agent_kill fix-auth
|
|
116
|
+
/agent_resume fix-auth Add error handling
|
|
117
|
+
/agent_resume --fork fix-auth Try a different approach
|
|
118
|
+
/agent_stats
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Notifications
|
|
124
|
+
|
|
125
|
+
The plugin sends targeted notifications to the originating Telegram thread:
|
|
126
|
+
|
|
127
|
+
| Emoji | Event | Description |
|
|
128
|
+
|-------|-------|-------------|
|
|
129
|
+
| 🚀 | Launched | Session started with prompt summary |
|
|
130
|
+
| 🔔 | Agent asks | Session is waiting for user input |
|
|
131
|
+
| 📋 | Plan ready | Plan approval requested — reply "go" to approve |
|
|
132
|
+
| 🔄 | Turn done | Turn completed, session paused (auto-resumable) |
|
|
133
|
+
| ✅ | Completed | Completion summary with cost and duration |
|
|
134
|
+
| ❌ | Failed | Error notification with hint |
|
|
135
|
+
| ⛔ | Killed | Session terminated with kill reason |
|
|
136
|
+
| 💤 | Idle-killed | Auto-resumes on next respond |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Plan → Execute Mode Switch
|
|
141
|
+
|
|
142
|
+
- **Claude Code** starts in `plan` mode by default. Approve a pending plan with `agent_respond(..., approve=true)` and the session switches to `bypassPermissions`.
|
|
143
|
+
- **Codex** does not surface `plan` or `awaiting-plan-approval` in session state. When launched with `permissionMode: "plan"`, its first turn is prompted to return a plan and ask whether to proceed, while the exposed session phase remains implementation-oriented.
|
|
144
|
+
|
|
145
|
+
On approval, the plugin prepends a system instruction telling the agent to exit plan mode and implement with full permissions.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Auto-Respond Rules
|
|
150
|
+
|
|
151
|
+
The orchestrator agent follows strict auto-respond rules to minimize noise:
|
|
152
|
+
|
|
153
|
+
**Auto-respond (immediate):**
|
|
154
|
+
- Permission requests (file read/write/bash) → "Yes, proceed."
|
|
155
|
+
- Explicit "should I continue?" confirmations → "Yes, continue."
|
|
156
|
+
|
|
157
|
+
**Forward to user (everything else):**
|
|
158
|
+
- Architecture/design decisions
|
|
159
|
+
- Destructive operations
|
|
160
|
+
- Scope changes
|
|
161
|
+
- Credential/production questions
|
|
162
|
+
- Any ambiguous or non-trivial question
|
|
163
|
+
|
|
164
|
+
When forwarding, the orchestrator quotes the agent's exact question without adding its own commentary.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Configuration
|
|
169
|
+
|
|
170
|
+
Set values in `~/.openclaw/openclaw.json` under `plugins.entries["openclaw-code-agent"].config`:
|
|
171
|
+
|
|
172
|
+
| Option | Type | Default | Description |
|
|
173
|
+
|--------|------|---------|-------------|
|
|
174
|
+
| `agentChannels` | `object` | — | Map workdir paths → notification channels (see [docs/AGENT_CHANNELS.md](docs/AGENT_CHANNELS.md)) |
|
|
175
|
+
| `fallbackChannel` | `string` | — | Default notification channel when no workspace match found |
|
|
176
|
+
| `maxSessions` | `number` | `5` | Maximum concurrent sessions |
|
|
177
|
+
| `maxAutoResponds` | `number` | `10` | Max consecutive auto-responds before requiring user input |
|
|
178
|
+
| `permissionMode` | `string` | `"plan"` | `"default"` / `"plan"` / `"acceptEdits"` / `"bypassPermissions"` |
|
|
179
|
+
| `idleTimeoutMinutes` | `number` | `15` | Idle timeout before auto-kill |
|
|
180
|
+
| `sessionGcAgeMinutes` | `number` | `1440` | TTL for completed/failed/killed runtime sessions before GC eviction |
|
|
181
|
+
| `maxPersistedSessions` | `number` | `50` | Max completed sessions kept for resume |
|
|
182
|
+
| `planApproval` | `string` | `"delegate"` | `"approve"` (orchestrator can auto-approve) / `"ask"` (always forward to user) / `"delegate"` (orchestrator decides) |
|
|
183
|
+
| `defaultHarness` | `string` | `"claude-code"` | Default harness for new sessions (`"claude-code"` / `"codex"`) |
|
|
184
|
+
| `model` | `string` | — | Codex-only model override for new sessions (for example `"gpt-5.3-codex"`). Used when no explicit `model` is passed to `agent_launch`; falls back to `defaultModel` if unset |
|
|
185
|
+
| `reasoningEffort` | `string` | `"medium"` | Codex-only reasoning effort: `"low"`, `"medium"`, or `"high"` |
|
|
186
|
+
| `defaultModel` | `string` | — | Default model for new sessions (e.g. `"sonnet"`, `"opus"`) |
|
|
187
|
+
| `defaultWorkdir` | `string` | — | Default working directory for new sessions |
|
|
188
|
+
|
|
189
|
+
### Permission Mode Mapping By Harness
|
|
190
|
+
|
|
191
|
+
Permission modes are shared at the plugin API, but each harness maps them differently:
|
|
192
|
+
|
|
193
|
+
- **Claude Code harness**
|
|
194
|
+
- `default`, `plan`, `acceptEdits`, `bypassPermissions` are passed through the SDK
|
|
195
|
+
- **Codex harness**
|
|
196
|
+
- Always runs with SDK thread options `sandboxMode: "danger-full-access"` and `approvalPolicy: "never"`
|
|
197
|
+
- Supports plugin config `model` and `reasoningEffort` defaults for Codex SDK thread launches
|
|
198
|
+
- In `bypassPermissions`, the harness adds filesystem root (`/` on POSIX) to Codex `additionalDirectories`, plus optional extras from `OPENCLAW_CODEX_BYPASS_ADDITIONAL_DIRS` (comma-separated)
|
|
199
|
+
- `setPermissionMode()` is applied by recreating the thread on the next turn via `resumeThread` (same thread ID)
|
|
200
|
+
- `plan` / `acceptEdits` remain behavioral orchestration constraints (planning/approval flow), not sandbox restrictions
|
|
201
|
+
|
|
202
|
+
### Runtime Environment Overrides
|
|
203
|
+
|
|
204
|
+
- `OPENCLAW_CODE_AGENT_SESSIONS_PATH` — explicit persisted session index path
|
|
205
|
+
- `OPENCLAW_HOME` — base dir for persisted session index when explicit path is unset (`$OPENCLAW_HOME/code-agent-sessions.json`)
|
|
206
|
+
- `OPENCLAW_CODEX_BYPASS_ADDITIONAL_DIRS` — comma-separated extra directories for Codex bypass mode
|
|
207
|
+
- `OPENCLAW_CODEX_HEARTBEAT_MS` — Codex activity heartbeat interval in milliseconds (default `10000`)
|
|
208
|
+
|
|
209
|
+
### `notify_on_turn_end` (`notifyOnTurnEnd`)
|
|
210
|
+
|
|
211
|
+
`agent_launch` accepts `notify_on_turn_end` (default `true`). When `false`, turn-end wake notifications are suppressed for that session.
|
|
212
|
+
Internal config field: `notifyOnTurnEnd`.
|
|
213
|
+
|
|
214
|
+
### Session Lifecycle + GC
|
|
215
|
+
|
|
216
|
+
- Active sessions live in runtime memory (`SessionManager.sessions`)
|
|
217
|
+
- Terminal sessions are persisted with metadata/output stubs for resume and listing
|
|
218
|
+
- Runtime records are evicted after `sessionGcAgeMinutes` (default 1440 / 24h)
|
|
219
|
+
- Eviction means **removed from runtime cache**, not deleted permanently; persisted session records remain resumable
|
|
220
|
+
|
|
221
|
+
### Example
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"plugins": {
|
|
226
|
+
"entries": {
|
|
227
|
+
"openclaw-code-agent": {
|
|
228
|
+
"enabled": true,
|
|
229
|
+
"config": {
|
|
230
|
+
"maxSessions": 3,
|
|
231
|
+
"model": "gpt-5.3-codex",
|
|
232
|
+
"reasoningEffort": "high",
|
|
233
|
+
"defaultModel": "sonnet",
|
|
234
|
+
"permissionMode": "plan",
|
|
235
|
+
"fallbackChannel": "telegram|my-bot|123456789",
|
|
236
|
+
"agentChannels": {
|
|
237
|
+
"/home/user/project-alpha": "telegram|my-bot|123456789",
|
|
238
|
+
"/home/user/project-beta": "telegram|ops-bot|987654321"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Orchestration Skill
|
|
250
|
+
|
|
251
|
+
<details>
|
|
252
|
+
<summary>Example orchestration skill (click to expand)</summary>
|
|
253
|
+
|
|
254
|
+
The plugin is a **transparent transport layer** — business logic lives in **OpenClaw skills**:
|
|
255
|
+
|
|
256
|
+
```markdown
|
|
257
|
+
---
|
|
258
|
+
name: Coding Agent Orchestrator
|
|
259
|
+
description: Orchestrates coding agent sessions with auto-response rules.
|
|
260
|
+
metadata: {"openclaw": {"requires": {"plugins": ["openclaw-code-agent"]}}}
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
# Coding Agent Orchestrator
|
|
264
|
+
|
|
265
|
+
## Anti-cascade rule
|
|
266
|
+
When woken by a waiting-for-input or completion event, ONLY use agent_respond
|
|
267
|
+
or agent_output for the referenced session. NEVER launch new sessions from wake events.
|
|
268
|
+
|
|
269
|
+
## Auto-response rules
|
|
270
|
+
|
|
271
|
+
When a coding agent session asks a question, analyze and decide:
|
|
272
|
+
|
|
273
|
+
### Auto-respond (use `agent_respond` immediately):
|
|
274
|
+
- Permission requests for file reads, writes, or bash commands -> "Yes, proceed."
|
|
275
|
+
- Confirmations like "Should I continue?" -> "Yes, continue."
|
|
276
|
+
|
|
277
|
+
### Forward to user (everything else):
|
|
278
|
+
- Architecture decisions, destructive operations, ambiguous requirements,
|
|
279
|
+
scope changes, credential/production questions
|
|
280
|
+
- Quote the agent's exact question. No commentary.
|
|
281
|
+
|
|
282
|
+
## Workflow
|
|
283
|
+
1. User sends a coding task -> `agent_launch(prompt, ...)`
|
|
284
|
+
2. Session runs in background. Monitor via wake events.
|
|
285
|
+
3. On wake event -> `agent_output` to read the question, then auto-respond or forward.
|
|
286
|
+
4. On completion -> summarize briefly: files changed, cost, duration, issues.
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
A comprehensive orchestration skill is available at [`skills/code-agent-orchestration/SKILL.md`](skills/code-agent-orchestration/SKILL.md).
|
|
290
|
+
|
|
291
|
+
</details>
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Architecture
|
|
296
|
+
|
|
297
|
+
For a detailed look at how the plugin works internally, see the [docs/](docs/) directory:
|
|
298
|
+
|
|
299
|
+
| Document | Description |
|
|
300
|
+
|----------|-------------|
|
|
301
|
+
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | System design, component breakdown, and data flow |
|
|
302
|
+
| [docs/NOTIFICATIONS.md](docs/NOTIFICATIONS.md) | Notification architecture, delivery model, and wake mechanism |
|
|
303
|
+
| [docs/AGENT_CHANNELS.md](docs/AGENT_CHANNELS.md) | Multi-agent setup, notification routing, and workspace mapping |
|
|
304
|
+
| [docs/TOOLS.md](docs/TOOLS.md) | Detailed tool reference with parameters and examples |
|
|
305
|
+
| [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) | Development guide, project structure, and build instructions |
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Development
|
|
310
|
+
|
|
311
|
+
Build output is an **ESM bundle** at `dist/index.js` (`package.json` has `"type": "module"`).
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# Install dependencies
|
|
315
|
+
pnpm install
|
|
316
|
+
|
|
317
|
+
# Build (esbuild → dist/index.js)
|
|
318
|
+
pnpm run build
|
|
319
|
+
|
|
320
|
+
# Type-check
|
|
321
|
+
pnpm run typecheck
|
|
322
|
+
|
|
323
|
+
# Run tests
|
|
324
|
+
pnpm test
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Project Structure
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
openclaw-code-agent/
|
|
331
|
+
├── index.ts # Plugin entry point
|
|
332
|
+
├── openclaw.plugin.json # Plugin manifest & config schema
|
|
333
|
+
├── src/
|
|
334
|
+
│ ├── harness/ # Agent harness abstraction layer
|
|
335
|
+
│ │ ├── types.ts # AgentHarness interface & message types
|
|
336
|
+
│ │ ├── claude-code.ts # Claude Code harness (SDK wrapper)
|
|
337
|
+
│ │ ├── codex.ts # Codex harness (@openai/codex-sdk thread stream wrapper)
|
|
338
|
+
│ │ └── index.ts # Harness registry
|
|
339
|
+
│ ├── types.ts # TypeScript interfaces
|
|
340
|
+
│ ├── config.ts # Config singleton + channel resolution
|
|
341
|
+
│ ├── format.ts # Formatting utilities
|
|
342
|
+
│ ├── singletons.ts # Module-level singleton refs
|
|
343
|
+
│ ├── session.ts # Session class (state machine, timers, harness)
|
|
344
|
+
│ ├── session-manager.ts # Session pool management + lifecycle
|
|
345
|
+
│ ├── session-store.ts # Persisted session/index storage abstraction
|
|
346
|
+
│ ├── session-metrics.ts # Metrics recorder abstraction
|
|
347
|
+
│ ├── wake-dispatcher.ts # Wake delivery + retry abstraction
|
|
348
|
+
│ ├── notifications.ts # Notification service
|
|
349
|
+
│ ├── actions/respond.ts # Shared respond logic (tool + command)
|
|
350
|
+
│ ├── application/ # Shared app-layer logic used by tools + commands
|
|
351
|
+
│ ├── tools/ # Tool implementations (6 tools)
|
|
352
|
+
│ └── commands/ # Chat command implementations (7 commands)
|
|
353
|
+
├── tests/ # Unit tests (node:test + tsx)
|
|
354
|
+
├── skills/ # Orchestration skill definitions
|
|
355
|
+
└── docs/ # Architecture & reference docs
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Contributing
|
|
361
|
+
|
|
362
|
+
Contributions are welcome! Please:
|
|
363
|
+
|
|
364
|
+
1. Fork the repository
|
|
365
|
+
2. Create a feature branch
|
|
366
|
+
3. Make your changes with tests
|
|
367
|
+
4. Submit a pull request
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
374
|
+
|
|
375
|
+
Originally based on [alizarion/openclaw-claude-code-plugin](https://github.com/alizarion/openclaw-claude-code-plugin). Renamed to `openclaw-code-agent` to be harness-agnostic.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var qi=Object.defineProperty;var qn=(e,t)=>{for(var n in t)qi(e,n,{get:t[n],enumerable:!0})};import{existsSync as Pp}from"fs";var X={};qn(X,{HasPropertyKey:()=>Qt,IsArray:()=>v,IsAsyncIterator:()=>zn,IsBigInt:()=>Ft,IsBoolean:()=>Ue,IsDate:()=>Ze,IsFunction:()=>Yn,IsIterator:()=>Xn,IsNull:()=>Jn,IsNumber:()=>ie,IsObject:()=>R,IsRegExp:()=>Et,IsString:()=>F,IsSymbol:()=>Qn,IsUint8Array:()=>Ne,IsUndefined:()=>j});function Qt(e,t){return t in e}function zn(e){return R(e)&&!v(e)&&!Ne(e)&&Symbol.asyncIterator in e}function v(e){return Array.isArray(e)}function Ft(e){return typeof e=="bigint"}function Ue(e){return typeof e=="boolean"}function Ze(e){return e instanceof globalThis.Date}function Yn(e){return typeof e=="function"}function Xn(e){return R(e)&&!v(e)&&!Ne(e)&&Symbol.iterator in e}function Jn(e){return e===null}function ie(e){return typeof e=="number"}function R(e){return typeof e=="object"&&e!==null}function Et(e){return e instanceof globalThis.RegExp}function F(e){return typeof e=="string"}function Qn(e){return typeof e=="symbol"}function Ne(e){return e instanceof globalThis.Uint8Array}function j(e){return e===void 0}function zi(e){return e.map(t=>Zt(t))}function Yi(e){return new Date(e.getTime())}function Xi(e){return new Uint8Array(e)}function Ji(e){return new RegExp(e.source,e.flags)}function Qi(e){let t={};for(let n of Object.getOwnPropertyNames(e))t[n]=Zt(e[n]);for(let n of Object.getOwnPropertySymbols(e))t[n]=Zt(e[n]);return t}function Zt(e){return v(e)?zi(e):Ze(e)?Yi(e):Ne(e)?Xi(e):Et(e)?Ji(e):R(e)?Qi(e):e}function _(e){return Zt(e)}function pt(e,t){return t===void 0?_(e):_({...t,...e})}function Gr(e){return e!==null&&typeof e=="object"}function Dr(e){return globalThis.Array.isArray(e)&&!globalThis.ArrayBuffer.isView(e)}function Vr(e){return e===void 0}function Br(e){return typeof e=="number"}var en;(function(e){e.InstanceMode="default",e.ExactOptionalPropertyTypes=!1,e.AllowArrayObject=!1,e.AllowNaN=!1,e.AllowNullVoid=!1;function t(a,p){return e.ExactOptionalPropertyTypes?p in a:a[p]!==void 0}e.IsExactOptionalProperty=t;function n(a){let p=Gr(a);return e.AllowArrayObject?p:p&&!Dr(a)}e.IsObjectLike=n;function r(a){return n(a)&&!(a instanceof Date)&&!(a instanceof Uint8Array)}e.IsRecordLike=r;function o(a){return e.AllowNaN?Br(a):Number.isFinite(a)}e.IsNumberLike=o;function s(a){let p=Vr(a);return e.AllowNullVoid?p||a===null:p}e.IsVoidLike=s})(en||(en={}));function Zi(e){return globalThis.Object.freeze(e).map(t=>kt(t))}function ea(e){let t={};for(let n of Object.getOwnPropertyNames(e))t[n]=kt(e[n]);for(let n of Object.getOwnPropertySymbols(e))t[n]=kt(e[n]);return globalThis.Object.freeze(t)}function kt(e){return v(e)?Zi(e):Ze(e)?e:Ne(e)?e:Et(e)?e:R(e)?ea(e):e}function u(e,t){let n=t!==void 0?{...t,...e}:e;switch(en.InstanceMode){case"freeze":return kt(n);case"clone":return _(n);default:return n}}var H=class extends Error{constructor(t){super(t)}};var D=Symbol.for("TypeBox.Transform"),xe=Symbol.for("TypeBox.Readonly"),Y=Symbol.for("TypeBox.Optional"),le=Symbol.for("TypeBox.Hint"),d=Symbol.for("TypeBox.Kind");function ct(e){return R(e)&&e[xe]==="Readonly"}function ee(e){return R(e)&&e[Y]==="Optional"}function Zn(e){return h(e,"Any")}function er(e){return h(e,"Argument")}function Se(e){return h(e,"Array")}function et(e){return h(e,"AsyncIterator")}function tt(e){return h(e,"BigInt")}function Ke(e){return h(e,"Boolean")}function Te(e){return h(e,"Computed")}function be(e){return h(e,"Constructor")}function ta(e){return h(e,"Date")}function Ae(e){return h(e,"Function")}function we(e){return h(e,"Integer")}function N(e){return h(e,"Intersect")}function nt(e){return h(e,"Iterator")}function h(e,t){return R(e)&&d in e&&e[d]===t}function tn(e){return Ue(e)||ie(e)||F(e)}function ae(e){return h(e,"Literal")}function ue(e){return h(e,"MappedKey")}function U(e){return h(e,"MappedResult")}function Ge(e){return h(e,"Never")}function na(e){return h(e,"Not")}function $t(e){return h(e,"Null")}function Oe(e){return h(e,"Number")}function G(e){return h(e,"Object")}function rt(e){return h(e,"Promise")}function ot(e){return h(e,"Record")}function L(e){return h(e,"Ref")}function tr(e){return h(e,"RegExp")}function Le(e){return h(e,"String")}function _t(e){return h(e,"Symbol")}function me(e){return h(e,"TemplateLiteral")}function ra(e){return h(e,"This")}function De(e){return R(e)&&D in e}function de(e){return h(e,"Tuple")}function Ut(e){return h(e,"Undefined")}function x(e){return h(e,"Union")}function oa(e){return h(e,"Uint8Array")}function sa(e){return h(e,"Unknown")}function ia(e){return h(e,"Unsafe")}function aa(e){return h(e,"Void")}function ua(e){return R(e)&&d in e&&F(e[d])}function pe(e){return Zn(e)||er(e)||Se(e)||Ke(e)||tt(e)||et(e)||Te(e)||be(e)||ta(e)||Ae(e)||we(e)||N(e)||nt(e)||ae(e)||ue(e)||U(e)||Ge(e)||na(e)||$t(e)||Oe(e)||G(e)||rt(e)||ot(e)||L(e)||tr(e)||Le(e)||_t(e)||me(e)||ra(e)||de(e)||Ut(e)||x(e)||oa(e)||sa(e)||ia(e)||aa(e)||ua(e)}var i={};qn(i,{IsAny:()=>zr,IsArgument:()=>Yr,IsArray:()=>Xr,IsAsyncIterator:()=>Jr,IsBigInt:()=>Qr,IsBoolean:()=>Zr,IsComputed:()=>eo,IsConstructor:()=>to,IsDate:()=>no,IsFunction:()=>ro,IsImport:()=>fa,IsInteger:()=>oo,IsIntersect:()=>so,IsIterator:()=>io,IsKind:()=>$o,IsKindOf:()=>I,IsLiteral:()=>Kt,IsLiteralBoolean:()=>ga,IsLiteralNumber:()=>uo,IsLiteralString:()=>ao,IsLiteralValue:()=>mo,IsMappedKey:()=>po,IsMappedResult:()=>co,IsNever:()=>lo,IsNot:()=>fo,IsNull:()=>go,IsNumber:()=>Io,IsObject:()=>ho,IsOptional:()=>la,IsPromise:()=>yo,IsProperties:()=>nn,IsReadonly:()=>ca,IsRecord:()=>xo,IsRecursive:()=>Ia,IsRef:()=>So,IsRegExp:()=>To,IsSchema:()=>W,IsString:()=>bo,IsSymbol:()=>Ao,IsTemplateLiteral:()=>wo,IsThis:()=>Oo,IsTransform:()=>Ro,IsTuple:()=>Co,IsUint8Array:()=>Mo,IsUndefined:()=>Po,IsUnion:()=>sr,IsUnionLiteral:()=>ha,IsUnknown:()=>Fo,IsUnsafe:()=>Eo,IsVoid:()=>ko,TypeGuardUnknownTypeError:()=>nr});var nr=class extends H{},ma=["Argument","Any","Array","AsyncIterator","BigInt","Boolean","Computed","Constructor","Date","Enum","Function","Integer","Intersect","Iterator","Literal","MappedKey","MappedResult","Not","Null","Number","Object","Promise","Record","Ref","RegExp","String","Symbol","TemplateLiteral","This","Tuple","Undefined","Union","Uint8Array","Unknown","Void"];function Hr(e){try{return new RegExp(e),!0}catch{return!1}}function rr(e){if(!F(e))return!1;for(let t=0;t<e.length;t++){let n=e.charCodeAt(t);if(n>=7&&n<=13||n===27||n===127)return!1}return!0}function Wr(e){return or(e)||W(e)}function Nt(e){return j(e)||Ft(e)}function E(e){return j(e)||ie(e)}function or(e){return j(e)||Ue(e)}function C(e){return j(e)||F(e)}function da(e){return j(e)||F(e)&&rr(e)&&Hr(e)}function pa(e){return j(e)||F(e)&&rr(e)}function qr(e){return j(e)||W(e)}function ca(e){return R(e)&&e[xe]==="Readonly"}function la(e){return R(e)&&e[Y]==="Optional"}function zr(e){return I(e,"Any")&&C(e.$id)}function Yr(e){return I(e,"Argument")&&ie(e.index)}function Xr(e){return I(e,"Array")&&e.type==="array"&&C(e.$id)&&W(e.items)&&E(e.minItems)&&E(e.maxItems)&&or(e.uniqueItems)&&qr(e.contains)&&E(e.minContains)&&E(e.maxContains)}function Jr(e){return I(e,"AsyncIterator")&&e.type==="AsyncIterator"&&C(e.$id)&&W(e.items)}function Qr(e){return I(e,"BigInt")&&e.type==="bigint"&&C(e.$id)&&Nt(e.exclusiveMaximum)&&Nt(e.exclusiveMinimum)&&Nt(e.maximum)&&Nt(e.minimum)&&Nt(e.multipleOf)}function Zr(e){return I(e,"Boolean")&&e.type==="boolean"&&C(e.$id)}function eo(e){return I(e,"Computed")&&F(e.target)&&v(e.parameters)&&e.parameters.every(t=>W(t))}function to(e){return I(e,"Constructor")&&e.type==="Constructor"&&C(e.$id)&&v(e.parameters)&&e.parameters.every(t=>W(t))&&W(e.returns)}function no(e){return I(e,"Date")&&e.type==="Date"&&C(e.$id)&&E(e.exclusiveMaximumTimestamp)&&E(e.exclusiveMinimumTimestamp)&&E(e.maximumTimestamp)&&E(e.minimumTimestamp)&&E(e.multipleOfTimestamp)}function ro(e){return I(e,"Function")&&e.type==="Function"&&C(e.$id)&&v(e.parameters)&&e.parameters.every(t=>W(t))&&W(e.returns)}function fa(e){return I(e,"Import")&&Qt(e,"$defs")&&R(e.$defs)&&nn(e.$defs)&&Qt(e,"$ref")&&F(e.$ref)&&e.$ref in e.$defs}function oo(e){return I(e,"Integer")&&e.type==="integer"&&C(e.$id)&&E(e.exclusiveMaximum)&&E(e.exclusiveMinimum)&&E(e.maximum)&&E(e.minimum)&&E(e.multipleOf)}function nn(e){return R(e)&&Object.entries(e).every(([t,n])=>rr(t)&&W(n))}function so(e){return I(e,"Intersect")&&!(F(e.type)&&e.type!=="object")&&v(e.allOf)&&e.allOf.every(t=>W(t)&&!Ro(t))&&C(e.type)&&(or(e.unevaluatedProperties)||qr(e.unevaluatedProperties))&&C(e.$id)}function io(e){return I(e,"Iterator")&&e.type==="Iterator"&&C(e.$id)&&W(e.items)}function I(e,t){return R(e)&&d in e&&e[d]===t}function ao(e){return Kt(e)&&F(e.const)}function uo(e){return Kt(e)&&ie(e.const)}function ga(e){return Kt(e)&&Ue(e.const)}function Kt(e){return I(e,"Literal")&&C(e.$id)&&mo(e.const)}function mo(e){return Ue(e)||ie(e)||F(e)}function po(e){return I(e,"MappedKey")&&v(e.keys)&&e.keys.every(t=>ie(t)||F(t))}function co(e){return I(e,"MappedResult")&&nn(e.properties)}function lo(e){return I(e,"Never")&&R(e.not)&&Object.getOwnPropertyNames(e.not).length===0}function fo(e){return I(e,"Not")&&W(e.not)}function go(e){return I(e,"Null")&&e.type==="null"&&C(e.$id)}function Io(e){return I(e,"Number")&&e.type==="number"&&C(e.$id)&&E(e.exclusiveMaximum)&&E(e.exclusiveMinimum)&&E(e.maximum)&&E(e.minimum)&&E(e.multipleOf)}function ho(e){return I(e,"Object")&&e.type==="object"&&C(e.$id)&&nn(e.properties)&&Wr(e.additionalProperties)&&E(e.minProperties)&&E(e.maxProperties)}function yo(e){return I(e,"Promise")&&e.type==="Promise"&&C(e.$id)&&W(e.item)}function xo(e){return I(e,"Record")&&e.type==="object"&&C(e.$id)&&Wr(e.additionalProperties)&&R(e.patternProperties)&&(t=>{let n=Object.getOwnPropertyNames(t.patternProperties);return n.length===1&&Hr(n[0])&&R(t.patternProperties)&&W(t.patternProperties[n[0]])})(e)}function Ia(e){return R(e)&&le in e&&e[le]==="Recursive"}function So(e){return I(e,"Ref")&&C(e.$id)&&F(e.$ref)}function To(e){return I(e,"RegExp")&&C(e.$id)&&F(e.source)&&F(e.flags)&&E(e.maxLength)&&E(e.minLength)}function bo(e){return I(e,"String")&&e.type==="string"&&C(e.$id)&&E(e.minLength)&&E(e.maxLength)&&da(e.pattern)&&pa(e.format)}function Ao(e){return I(e,"Symbol")&&e.type==="symbol"&&C(e.$id)}function wo(e){return I(e,"TemplateLiteral")&&e.type==="string"&&F(e.pattern)&&e.pattern[0]==="^"&&e.pattern[e.pattern.length-1]==="$"}function Oo(e){return I(e,"This")&&C(e.$id)&&F(e.$ref)}function Ro(e){return R(e)&&D in e}function Co(e){return I(e,"Tuple")&&e.type==="array"&&C(e.$id)&&ie(e.minItems)&&ie(e.maxItems)&&e.minItems===e.maxItems&&(j(e.items)&&j(e.additionalItems)&&e.minItems===0||v(e.items)&&e.items.every(t=>W(t)))}function Po(e){return I(e,"Undefined")&&e.type==="undefined"&&C(e.$id)}function ha(e){return sr(e)&&e.anyOf.every(t=>ao(t)||uo(t))}function sr(e){return I(e,"Union")&&C(e.$id)&&R(e)&&v(e.anyOf)&&e.anyOf.every(t=>W(t))}function Mo(e){return I(e,"Uint8Array")&&e.type==="Uint8Array"&&C(e.$id)&&E(e.minByteLength)&&E(e.maxByteLength)}function Fo(e){return I(e,"Unknown")&&C(e.$id)}function Eo(e){return I(e,"Unsafe")}function ko(e){return I(e,"Void")&&e.type==="void"&&C(e.$id)}function $o(e){return R(e)&&d in e&&F(e[d])&&!ma.includes(e[d])}function W(e){return R(e)&&(zr(e)||Yr(e)||Xr(e)||Zr(e)||Qr(e)||Jr(e)||eo(e)||to(e)||no(e)||ro(e)||oo(e)||so(e)||io(e)||Kt(e)||po(e)||co(e)||lo(e)||fo(e)||go(e)||Io(e)||ho(e)||yo(e)||xo(e)||So(e)||To(e)||bo(e)||Ao(e)||wo(e)||Oo(e)||Co(e)||Po(e)||sr(e)||Mo(e)||Fo(e)||Eo(e)||ko(e)||$o(e))}var ir="(true|false)",Lt="(0|[1-9][0-9]*)",ar="(.*)",ya="(?!.*)",$l=`^${ir}$`,Ve=`^${Lt}$`,Be=`^${ar}$`,_o=`^${ya}$`;function Uo(e,t){return e.includes(t)}function No(e){return[...new Set(e)]}function xa(e,t){return e.filter(n=>t.includes(n))}function Sa(e,t){return e.reduce((n,r)=>xa(n,r),t)}function Ko(e){return e.length===1?e[0]:e.length>1?Sa(e.slice(1),e[0]):[]}function Lo(e){let t=[];for(let n of e)t.push(...n);return t}function He(e){return u({[d]:"Any"},e)}function lt(e,t){return u({[d]:"Array",type:"array",items:e},t)}function vo(e){return u({[d]:"Argument",index:e})}function ft(e,t){return u({[d]:"AsyncIterator",type:"AsyncIterator",items:e},t)}function k(e,t,n){return u({[d]:"Computed",target:e,parameters:t},n)}function Ta(e,t){let{[t]:n,...r}=e;return r}function K(e,t){return t.reduce((n,r)=>Ta(n,r),e)}function S(e){return u({[d]:"Never",not:{}},e)}function T(e){return u({[d]:"MappedResult",properties:e})}function gt(e,t,n){return u({[d]:"Constructor",type:"Constructor",parameters:e,returns:t},n)}function Ee(e,t,n){return u({[d]:"Function",type:"Function",parameters:e,returns:t},n)}function vt(e,t){return u({[d]:"Union",anyOf:e},t)}function ba(e){return e.some(t=>ee(t))}function jo(e){return e.map(t=>ee(t)?Aa(t):t)}function Aa(e){return K(e,[Y])}function wa(e,t){return ba(e)?J(vt(jo(e),t)):vt(jo(e),t)}function ke(e,t){return e.length===1?u(e[0],t):e.length===0?S(t):wa(e,t)}function w(e,t){return e.length===0?S(t):e.length===1?u(e[0],t):vt(e,t)}var rn=class extends H{};function Oa(e){return e.replace(/\\\$/g,"$").replace(/\\\*/g,"*").replace(/\\\^/g,"^").replace(/\\\|/g,"|").replace(/\\\(/g,"(").replace(/\\\)/g,")")}function ur(e,t,n){return e[t]===n&&e.charCodeAt(t-1)!==92}function je(e,t){return ur(e,t,"(")}function jt(e,t){return ur(e,t,")")}function Go(e,t){return ur(e,t,"|")}function Ra(e){if(!(je(e,0)&&jt(e,e.length-1)))return!1;let t=0;for(let n=0;n<e.length;n++)if(je(e,n)&&(t+=1),jt(e,n)&&(t-=1),t===0&&n!==e.length-1)return!1;return!0}function Ca(e){return e.slice(1,e.length-1)}function Pa(e){let t=0;for(let n=0;n<e.length;n++)if(je(e,n)&&(t+=1),jt(e,n)&&(t-=1),Go(e,n)&&t===0)return!0;return!1}function Ma(e){for(let t=0;t<e.length;t++)if(je(e,t))return!0;return!1}function Fa(e){let[t,n]=[0,0],r=[];for(let s=0;s<e.length;s++)if(je(e,s)&&(t+=1),jt(e,s)&&(t-=1),Go(e,s)&&t===0){let a=e.slice(n,s);a.length>0&&r.push(It(a)),n=s+1}let o=e.slice(n);return o.length>0&&r.push(It(o)),r.length===0?{type:"const",const:""}:r.length===1?r[0]:{type:"or",expr:r}}function Ea(e){function t(o,s){if(!je(o,s))throw new rn("TemplateLiteralParser: Index must point to open parens");let a=0;for(let p=s;p<o.length;p++)if(je(o,p)&&(a+=1),jt(o,p)&&(a-=1),a===0)return[s,p];throw new rn("TemplateLiteralParser: Unclosed group parens in expression")}function n(o,s){for(let a=s;a<o.length;a++)if(je(o,a))return[s,a];return[s,o.length]}let r=[];for(let o=0;o<e.length;o++)if(je(e,o)){let[s,a]=t(e,o),p=e.slice(s,a+1);r.push(It(p)),o=a}else{let[s,a]=n(e,o),p=e.slice(s,a);p.length>0&&r.push(It(p)),o=a-1}return r.length===0?{type:"const",const:""}:r.length===1?r[0]:{type:"and",expr:r}}function It(e){return Ra(e)?It(Ca(e)):Pa(e)?Fa(e):Ma(e)?Ea(e):{type:"const",const:Oa(e)}}function ht(e){return It(e.slice(1,e.length-1))}var mr=class extends H{};function ka(e){return e.type==="or"&&e.expr.length===2&&e.expr[0].type==="const"&&e.expr[0].const==="0"&&e.expr[1].type==="const"&&e.expr[1].const==="[1-9][0-9]*"}function $a(e){return e.type==="or"&&e.expr.length===2&&e.expr[0].type==="const"&&e.expr[0].const==="true"&&e.expr[1].type==="const"&&e.expr[1].const==="false"}function _a(e){return e.type==="const"&&e.const===".*"}function st(e){return ka(e)||_a(e)?!1:$a(e)?!0:e.type==="and"?e.expr.every(t=>st(t)):e.type==="or"?e.expr.every(t=>st(t)):e.type==="const"?!0:(()=>{throw new mr("Unknown expression type")})()}function Do(e){let t=ht(e.pattern);return st(t)}var dr=class extends H{};function*Vo(e){if(e.length===1)return yield*e[0];for(let t of e[0])for(let n of Vo(e.slice(1)))yield`${t}${n}`}function*Ua(e){return yield*Vo(e.expr.map(t=>[...Gt(t)]))}function*Na(e){for(let t of e.expr)yield*Gt(t)}function*Ka(e){return yield e.const}function*Gt(e){return e.type==="and"?yield*Ua(e):e.type==="or"?yield*Na(e):e.type==="const"?yield*Ka(e):(()=>{throw new dr("Unknown expression")})()}function on(e){let t=ht(e.pattern);return st(t)?[...Gt(t)]:[]}function A(e,t){return u({[d]:"Literal",const:e,type:typeof e},t)}function sn(e){return u({[d]:"Boolean",type:"boolean"},e)}function yt(e){return u({[d]:"BigInt",type:"bigint"},e)}function fe(e){return u({[d]:"Number",type:"number"},e)}function Re(e){return u({[d]:"String",type:"string"},e)}function*La(e){let t=e.trim().replace(/"|'/g,"");return t==="boolean"?yield sn():t==="number"?yield fe():t==="bigint"?yield yt():t==="string"?yield Re():yield(()=>{let n=t.split("|").map(r=>A(r.trim()));return n.length===0?S():n.length===1?n[0]:ke(n)})()}function*va(e){if(e[1]!=="{"){let t=A("$"),n=pr(e.slice(1));return yield*[t,...n]}for(let t=2;t<e.length;t++)if(e[t]==="}"){let n=La(e.slice(2,t)),r=pr(e.slice(t+1));return yield*[...n,...r]}yield A(e)}function*pr(e){for(let t=0;t<e.length;t++)if(e[t]==="$"){let n=A(e.slice(0,t)),r=va(e.slice(t));return yield*[n,...r]}yield A(e)}function Bo(e){return[...pr(e)]}var cr=class extends H{};function ja(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Ho(e,t){return me(e)?e.pattern.slice(1,e.pattern.length-1):x(e)?`(${e.anyOf.map(n=>Ho(n,t)).join("|")})`:Oe(e)?`${t}${Lt}`:we(e)?`${t}${Lt}`:tt(e)?`${t}${Lt}`:Le(e)?`${t}${ar}`:ae(e)?`${t}${ja(e.const.toString())}`:Ke(e)?`${t}${ir}`:(()=>{throw new cr(`Unexpected Kind '${e[d]}'`)})()}function lr(e){return`^${e.map(t=>Ho(t,"")).join("")}$`}function it(e){let n=on(e).map(r=>A(r));return ke(n)}function an(e,t){let n=F(e)?lr(Bo(e)):lr(e);return u({[d]:"TemplateLiteral",type:"string",pattern:n},t)}function Ga(e){return on(e).map(n=>n.toString())}function Da(e){let t=[];for(let n of e)t.push(...te(n));return t}function Va(e){return[e.toString()]}function te(e){return[...new Set(me(e)?Ga(e):x(e)?Da(e.anyOf):ae(e)?Va(e.const):Oe(e)?["[number]"]:we(e)?["[number]"]:[])]}function Ba(e,t,n){let r={};for(let o of Object.getOwnPropertyNames(t))r[o]=We(e,te(t[o]),n);return r}function Ha(e,t,n){return Ba(e,t.properties,n)}function Wo(e,t,n){let r=Ha(e,t,n);return T(r)}function zo(e,t){return e.map(n=>Yo(n,t))}function Wa(e){return e.filter(t=>!Ge(t))}function qa(e,t){return un(Wa(zo(e,t)))}function za(e){return e.some(t=>Ge(t))?[]:e}function Ya(e,t){return ke(za(zo(e,t)))}function Xa(e,t){return t in e?e[t]:t==="[number]"?ke(e):S()}function Ja(e,t){return t==="[number]"?e:S()}function Qa(e,t){return t in e?e[t]:S()}function Yo(e,t){return N(e)?qa(e.allOf,t):x(e)?Ya(e.anyOf,t):de(e)?Xa(e.items??[],t):Se(e)?Ja(e.items,t):G(e)?Qa(e.properties,t):S()}function fr(e,t){return t.map(n=>Yo(e,n))}function qo(e,t){return ke(fr(e,t))}function We(e,t,n){if(L(e)||L(t)){let r="Index types using Ref parameters require both Type and Key to be of TSchema";if(!pe(e)||!pe(t))throw new H(r);return k("Index",[e,t])}return U(t)?Wo(e,t,n):ue(t)?Xo(e,t,n):u(pe(t)?qo(e,te(t)):qo(e,t),n)}function Za(e,t,n){return{[t]:We(e,[t],_(n))}}function eu(e,t,n){return t.reduce((r,o)=>({...r,...Za(e,o,n)}),{})}function tu(e,t,n){return eu(e,t.keys,n)}function Xo(e,t,n){let r=tu(e,t,n);return T(r)}function xt(e,t){return u({[d]:"Iterator",type:"Iterator",items:e},t)}function nu(e){return globalThis.Object.keys(e).filter(t=>!ee(e[t]))}function ru(e,t){let n=nu(e),r=n.length>0?{[d]:"Object",type:"object",required:n,properties:e}:{[d]:"Object",type:"object",properties:e};return u(r,t)}var P=ru;function mn(e,t){return u({[d]:"Promise",type:"Promise",item:e},t)}function ou(e){return u(K(e,[xe]))}function su(e){return u({...e,[xe]:"Readonly"})}function iu(e,t){return t===!1?ou(e):su(e)}function ne(e,t){let n=t??!0;return U(e)?Jo(e,n):iu(e,n)}function au(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(e))n[r]=ne(e[r],t);return n}function uu(e,t){return au(e.properties,t)}function Jo(e,t){let n=uu(e,t);return T(n)}function ge(e,t){return u(e.length>0?{[d]:"Tuple",type:"array",items:e,additionalItems:!1,minItems:e.length,maxItems:e.length}:{[d]:"Tuple",type:"array",minItems:e.length,maxItems:e.length},t)}function Qo(e,t){return e in t?Ie(e,t[e]):T(t)}function mu(e){return{[e]:A(e)}}function du(e){let t={};for(let n of e)t[n]=A(n);return t}function pu(e,t){return Uo(t,e)?mu(e):du(t)}function cu(e,t){let n=pu(e,t);return Qo(e,n)}function Dt(e,t){return t.map(n=>Ie(e,n))}function lu(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(t))n[r]=Ie(e,t[r]);return n}function Ie(e,t){let n={...t};return ee(t)?J(Ie(e,K(t,[Y]))):ct(t)?ne(Ie(e,K(t,[xe]))):U(t)?Qo(e,t.properties):ue(t)?cu(e,t.keys):be(t)?gt(Dt(e,t.parameters),Ie(e,t.returns),n):Ae(t)?Ee(Dt(e,t.parameters),Ie(e,t.returns),n):et(t)?ft(Ie(e,t.items),n):nt(t)?xt(Ie(e,t.items),n):N(t)?Q(Dt(e,t.allOf),n):x(t)?w(Dt(e,t.anyOf),n):de(t)?ge(Dt(e,t.items??[]),n):G(t)?P(lu(e,t.properties),n):Se(t)?lt(Ie(e,t.items),n):rt(t)?mn(Ie(e,t.item),n):t}function fu(e,t){let n={};for(let r of e)n[r]=Ie(r,t);return n}function Zo(e,t,n){let r=pe(e)?te(e):e,o=t({[d]:"MappedKey",keys:r}),s=fu(r,o);return P(s,n)}function gu(e){return u(K(e,[Y]))}function Iu(e){return u({...e,[Y]:"Optional"})}function hu(e,t){return t===!1?gu(e):Iu(e)}function J(e,t){let n=t??!0;return U(e)?es(e,n):hu(e,n)}function yu(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(e))n[r]=J(e[r],t);return n}function xu(e,t){return yu(e.properties,t)}function es(e,t){let n=xu(e,t);return T(n)}function Vt(e,t={}){let n=e.every(o=>G(o)),r=pe(t.unevaluatedProperties)?{unevaluatedProperties:t.unevaluatedProperties}:{};return u(t.unevaluatedProperties===!1||pe(t.unevaluatedProperties)||n?{...r,[d]:"Intersect",type:"object",allOf:e}:{...r,[d]:"Intersect",allOf:e},t)}function Su(e){return e.every(t=>ee(t))}function Tu(e){return K(e,[Y])}function ts(e){return e.map(t=>ee(t)?Tu(t):t)}function bu(e,t){return Su(e)?J(Vt(ts(e),t)):Vt(ts(e),t)}function un(e,t={}){if(e.length===1)return u(e[0],t);if(e.length===0)return S(t);if(e.some(n=>De(n)))throw new Error("Cannot intersect transform types");return bu(e,t)}function Q(e,t){if(e.length===1)return u(e[0],t);if(e.length===0)return S(t);if(e.some(n=>De(n)))throw new Error("Cannot intersect transform types");return Vt(e,t)}function $e(...e){let[t,n]=typeof e[0]=="string"?[e[0],e[1]]:[e[0].$id,e[1]];if(typeof t!="string")throw new H("Ref: $ref must be a string");return u({[d]:"Ref",$ref:t},n)}function Au(e,t){return k("Awaited",[k(e,t)])}function wu(e){return k("Awaited",[$e(e)])}function Ou(e){return Q(ns(e))}function Ru(e){return w(ns(e))}function Cu(e){return St(e)}function ns(e){return e.map(t=>St(t))}function St(e,t){return u(Te(e)?Au(e.target,e.parameters):N(e)?Ou(e.allOf):x(e)?Ru(e.anyOf):rt(e)?Cu(e.item):L(e)?wu(e.$ref):e,t)}function rs(e){let t=[];for(let n of e)t.push(Bt(n));return t}function Pu(e){let t=rs(e);return Lo(t)}function Mu(e){let t=rs(e);return Ko(t)}function Fu(e){return e.map((t,n)=>n.toString())}function Eu(e){return["[number]"]}function ku(e){return globalThis.Object.getOwnPropertyNames(e)}function $u(e){return _u?globalThis.Object.getOwnPropertyNames(e).map(n=>n[0]==="^"&&n[n.length-1]==="$"?n.slice(1,n.length-1):n):[]}function Bt(e){return N(e)?Pu(e.allOf):x(e)?Mu(e.anyOf):de(e)?Fu(e.items??[]):Se(e)?Eu(e.items):G(e)?ku(e.properties):ot(e)?$u(e.patternProperties):[]}var _u=!1;function Uu(e,t){return k("KeyOf",[k(e,t)])}function Nu(e){return k("KeyOf",[$e(e)])}function Ku(e,t){let n=Bt(e),r=Lu(n),o=ke(r);return u(o,t)}function Lu(e){return e.map(t=>t==="[number]"?fe():A(t))}function Tt(e,t){return Te(e)?Uu(e.target,e.parameters):L(e)?Nu(e.$ref):U(e)?os(e,t):Ku(e,t)}function vu(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(e))n[r]=Tt(e[r],_(t));return n}function ju(e,t){return vu(e.properties,t)}function os(e,t){let n=ju(e,t);return T(n)}function Gu(e){let t=[];for(let n of e)t.push(...Bt(n));return No(t)}function Du(e){return e.filter(t=>!Ge(t))}function Vu(e,t){let n=[];for(let r of e)n.push(...fr(r,[t]));return Du(n)}function Bu(e,t){let n={};for(let r of t)n[r]=un(Vu(e,r));return n}function ss(e,t){let n=Gu(e),r=Bu(e,n);return P(r,t)}function dn(e){return u({[d]:"Date",type:"Date"},e)}function pn(e){return u({[d]:"Null",type:"null"},e)}function cn(e){return u({[d]:"Symbol",type:"symbol"},e)}function ln(e){return u({[d]:"Undefined",type:"undefined"},e)}function fn(e){return u({[d]:"Uint8Array",type:"Uint8Array"},e)}function qe(e){return u({[d]:"Unknown"},e)}function Hu(e){return e.map(t=>gr(t,!1))}function Wu(e){let t={};for(let n of globalThis.Object.getOwnPropertyNames(e))t[n]=ne(gr(e[n],!1));return t}function gn(e,t){return t===!0?e:ne(e)}function gr(e,t){return zn(e)?gn(He(),t):Xn(e)?gn(He(),t):v(e)?ne(ge(Hu(e))):Ne(e)?fn():Ze(e)?dn():R(e)?gn(P(Wu(e)),t):Yn(e)?gn(Ee([],qe()),t):j(e)?ln():Jn(e)?pn():Qn(e)?cn():Ft(e)?yt():ie(e)?A(e):Ue(e)?A(e):F(e)?A(e):P({})}function is(e,t){return u(gr(e,!0),t)}function as(e,t){return be(e)?ge(e.parameters,t):S(t)}function us(e,t){if(j(e))throw new Error("Enum undefined or empty");let n=globalThis.Object.getOwnPropertyNames(e).filter(s=>isNaN(s)).map(s=>e[s]),o=[...new Set(n)].map(s=>A(s));return w(o,{...t,[le]:"Enum"})}var hr=class extends H{},m;(function(e){e[e.Union=0]="Union",e[e.True=1]="True",e[e.False=2]="False"})(m||(m={}));function he(e){return e===m.False?e:m.True}function bt(e){throw new hr(e)}function V(e){return i.IsNever(e)||i.IsIntersect(e)||i.IsUnion(e)||i.IsUnknown(e)||i.IsAny(e)}function B(e,t){return i.IsNever(t)?gs(e,t):i.IsIntersect(t)?In(e,t):i.IsUnion(t)?Tr(e,t):i.IsUnknown(t)?xs(e,t):i.IsAny(t)?Sr(e,t):bt("StructuralRight")}function Sr(e,t){return m.True}function qu(e,t){return i.IsIntersect(t)?In(e,t):i.IsUnion(t)&&t.anyOf.some(n=>i.IsAny(n)||i.IsUnknown(n))?m.True:i.IsUnion(t)?m.Union:i.IsUnknown(t)||i.IsAny(t)?m.True:m.Union}function zu(e,t){return i.IsUnknown(e)?m.False:i.IsAny(e)?m.Union:i.IsNever(e)?m.True:m.False}function Yu(e,t){return i.IsObject(t)&&hn(t)?m.True:V(t)?B(e,t):i.IsArray(t)?he(M(e.items,t.items)):m.False}function Xu(e,t){return V(t)?B(e,t):i.IsAsyncIterator(t)?he(M(e.items,t.items)):m.False}function Ju(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsBigInt(t)?m.True:m.False}function ls(e,t){return i.IsLiteralBoolean(e)||i.IsBoolean(e)?m.True:m.False}function Qu(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsBoolean(t)?m.True:m.False}function Zu(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsConstructor(t)?e.parameters.length>t.parameters.length?m.False:e.parameters.every((n,r)=>he(M(t.parameters[r],n))===m.True)?he(M(e.returns,t.returns)):m.False:m.False}function em(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsDate(t)?m.True:m.False}function tm(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsFunction(t)?e.parameters.length>t.parameters.length?m.False:e.parameters.every((n,r)=>he(M(t.parameters[r],n))===m.True)?he(M(e.returns,t.returns)):m.False:m.False}function fs(e,t){return i.IsLiteral(e)&&X.IsNumber(e.const)||i.IsNumber(e)||i.IsInteger(e)?m.True:m.False}function nm(e,t){return i.IsInteger(t)||i.IsNumber(t)?m.True:V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):m.False}function In(e,t){return t.allOf.every(n=>M(e,n)===m.True)?m.True:m.False}function rm(e,t){return e.allOf.some(n=>M(n,t)===m.True)?m.True:m.False}function om(e,t){return V(t)?B(e,t):i.IsIterator(t)?he(M(e.items,t.items)):m.False}function sm(e,t){return i.IsLiteral(t)&&t.const===e.const?m.True:V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsString(t)?ys(e,t):i.IsNumber(t)?Is(e,t):i.IsInteger(t)?fs(e,t):i.IsBoolean(t)?ls(e,t):m.False}function gs(e,t){return m.False}function im(e,t){return m.True}function ms(e){let[t,n]=[e,0];for(;i.IsNot(t);)t=t.not,n+=1;return n%2===0?t:qe()}function am(e,t){return i.IsNot(e)?M(ms(e),t):i.IsNot(t)?M(e,ms(t)):bt("Invalid fallthrough for Not")}function um(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsNull(t)?m.True:m.False}function Is(e,t){return i.IsLiteralNumber(e)||i.IsNumber(e)||i.IsInteger(e)?m.True:m.False}function mm(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsInteger(t)||i.IsNumber(t)?m.True:m.False}function re(e,t){return Object.getOwnPropertyNames(e.properties).length===t}function ds(e){return hn(e)}function ps(e){return re(e,0)||re(e,1)&&"description"in e.properties&&i.IsUnion(e.properties.description)&&e.properties.description.anyOf.length===2&&(i.IsString(e.properties.description.anyOf[0])&&i.IsUndefined(e.properties.description.anyOf[1])||i.IsString(e.properties.description.anyOf[1])&&i.IsUndefined(e.properties.description.anyOf[0]))}function Ir(e){return re(e,0)}function cs(e){return re(e,0)}function dm(e){return re(e,0)}function pm(e){return re(e,0)}function cm(e){return hn(e)}function lm(e){let t=fe();return re(e,0)||re(e,1)&&"length"in e.properties&&he(M(e.properties.length,t))===m.True}function fm(e){return re(e,0)}function hn(e){let t=fe();return re(e,0)||re(e,1)&&"length"in e.properties&&he(M(e.properties.length,t))===m.True}function gm(e){let t=Ee([He()],He());return re(e,0)||re(e,1)&&"then"in e.properties&&he(M(e.properties.then,t))===m.True}function hs(e,t){return M(e,t)===m.False||i.IsOptional(e)&&!i.IsOptional(t)?m.False:m.True}function Z(e,t){return i.IsUnknown(e)?m.False:i.IsAny(e)?m.Union:i.IsNever(e)||i.IsLiteralString(e)&&ds(t)||i.IsLiteralNumber(e)&&Ir(t)||i.IsLiteralBoolean(e)&&cs(t)||i.IsSymbol(e)&&ps(t)||i.IsBigInt(e)&&dm(t)||i.IsString(e)&&ds(t)||i.IsSymbol(e)&&ps(t)||i.IsNumber(e)&&Ir(t)||i.IsInteger(e)&&Ir(t)||i.IsBoolean(e)&&cs(t)||i.IsUint8Array(e)&&cm(t)||i.IsDate(e)&&pm(t)||i.IsConstructor(e)&&fm(t)||i.IsFunction(e)&&lm(t)?m.True:i.IsRecord(e)&&i.IsString(yr(e))?t[le]==="Record"?m.True:m.False:i.IsRecord(e)&&i.IsNumber(yr(e))&&re(t,0)?m.True:m.False}function Im(e,t){return V(t)?B(e,t):i.IsRecord(t)?ye(e,t):i.IsObject(t)?(()=>{for(let n of Object.getOwnPropertyNames(t.properties)){if(!(n in e.properties)&&!i.IsOptional(t.properties[n]))return m.False;if(i.IsOptional(t.properties[n]))return m.True;if(hs(e.properties[n],t.properties[n])===m.False)return m.False}return m.True})():m.False}function hm(e,t){return V(t)?B(e,t):i.IsObject(t)&&gm(t)?m.True:i.IsPromise(t)?he(M(e.item,t.item)):m.False}function yr(e){return Ve in e.patternProperties?fe():Be in e.patternProperties?Re():bt("Unknown record key pattern")}function xr(e){return Ve in e.patternProperties?e.patternProperties[Ve]:Be in e.patternProperties?e.patternProperties[Be]:bt("Unable to get record value schema")}function ye(e,t){let[n,r]=[yr(t),xr(t)];return i.IsLiteralString(e)&&i.IsNumber(n)&&he(M(e,r))===m.True?m.True:i.IsUint8Array(e)&&i.IsNumber(n)||i.IsString(e)&&i.IsNumber(n)||i.IsArray(e)&&i.IsNumber(n)?M(e,r):i.IsObject(e)?(()=>{for(let o of Object.getOwnPropertyNames(e.properties))if(hs(r,e.properties[o])===m.False)return m.False;return m.True})():m.False}function ym(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?M(xr(e),xr(t)):m.False}function xm(e,t){let n=i.IsRegExp(e)?Re():e,r=i.IsRegExp(t)?Re():t;return M(n,r)}function ys(e,t){return i.IsLiteral(e)&&X.IsString(e.const)||i.IsString(e)?m.True:m.False}function Sm(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsString(t)?m.True:m.False}function Tm(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsSymbol(t)?m.True:m.False}function bm(e,t){return i.IsTemplateLiteral(e)?M(it(e),t):i.IsTemplateLiteral(t)?M(e,it(t)):bt("Invalid fallthrough for TemplateLiteral")}function Am(e,t){return i.IsArray(t)&&e.items!==void 0&&e.items.every(n=>M(n,t.items)===m.True)}function wm(e,t){return i.IsNever(e)?m.True:i.IsUnknown(e)?m.False:i.IsAny(e)?m.Union:m.False}function Om(e,t){return V(t)?B(e,t):i.IsObject(t)&&hn(t)||i.IsArray(t)&&Am(e,t)?m.True:i.IsTuple(t)?X.IsUndefined(e.items)&&!X.IsUndefined(t.items)||!X.IsUndefined(e.items)&&X.IsUndefined(t.items)?m.False:X.IsUndefined(e.items)&&!X.IsUndefined(t.items)||e.items.every((n,r)=>M(n,t.items[r])===m.True)?m.True:m.False:m.False}function Rm(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsUint8Array(t)?m.True:m.False}function Cm(e,t){return V(t)?B(e,t):i.IsObject(t)?Z(e,t):i.IsRecord(t)?ye(e,t):i.IsVoid(t)?Fm(e,t):i.IsUndefined(t)?m.True:m.False}function Tr(e,t){return t.anyOf.some(n=>M(e,n)===m.True)?m.True:m.False}function Pm(e,t){return e.anyOf.every(n=>M(n,t)===m.True)?m.True:m.False}function xs(e,t){return m.True}function Mm(e,t){return i.IsNever(t)?gs(e,t):i.IsIntersect(t)?In(e,t):i.IsUnion(t)?Tr(e,t):i.IsAny(t)?Sr(e,t):i.IsString(t)?ys(e,t):i.IsNumber(t)?Is(e,t):i.IsInteger(t)?fs(e,t):i.IsBoolean(t)?ls(e,t):i.IsArray(t)?zu(e,t):i.IsTuple(t)?wm(e,t):i.IsObject(t)?Z(e,t):i.IsUnknown(t)?m.True:m.False}function Fm(e,t){return i.IsUndefined(e)||i.IsUndefined(e)?m.True:m.False}function Em(e,t){return i.IsIntersect(t)?In(e,t):i.IsUnion(t)?Tr(e,t):i.IsUnknown(t)?xs(e,t):i.IsAny(t)?Sr(e,t):i.IsObject(t)?Z(e,t):i.IsVoid(t)?m.True:m.False}function M(e,t){return i.IsTemplateLiteral(e)||i.IsTemplateLiteral(t)?bm(e,t):i.IsRegExp(e)||i.IsRegExp(t)?xm(e,t):i.IsNot(e)||i.IsNot(t)?am(e,t):i.IsAny(e)?qu(e,t):i.IsArray(e)?Yu(e,t):i.IsBigInt(e)?Ju(e,t):i.IsBoolean(e)?Qu(e,t):i.IsAsyncIterator(e)?Xu(e,t):i.IsConstructor(e)?Zu(e,t):i.IsDate(e)?em(e,t):i.IsFunction(e)?tm(e,t):i.IsInteger(e)?nm(e,t):i.IsIntersect(e)?rm(e,t):i.IsIterator(e)?om(e,t):i.IsLiteral(e)?sm(e,t):i.IsNever(e)?im(e,t):i.IsNull(e)?um(e,t):i.IsNumber(e)?mm(e,t):i.IsObject(e)?Im(e,t):i.IsRecord(e)?ym(e,t):i.IsString(e)?Sm(e,t):i.IsSymbol(e)?Tm(e,t):i.IsTuple(e)?Om(e,t):i.IsPromise(e)?hm(e,t):i.IsUint8Array(e)?Rm(e,t):i.IsUndefined(e)?Cm(e,t):i.IsUnion(e)?Pm(e,t):i.IsUnknown(e)?Mm(e,t):i.IsVoid(e)?Em(e,t):bt(`Unknown left type operand '${e[d]}'`)}function ze(e,t){return M(e,t)}function km(e,t,n,r,o){let s={};for(let a of globalThis.Object.getOwnPropertyNames(e))s[a]=At(e[a],t,n,r,_(o));return s}function $m(e,t,n,r,o){return km(e.properties,t,n,r,o)}function Ss(e,t,n,r,o){let s=$m(e,t,n,r,o);return T(s)}function _m(e,t,n,r){let o=ze(e,t);return o===m.Union?w([n,r]):o===m.True?n:r}function At(e,t,n,r,o){return U(e)?Ss(e,t,n,r,o):ue(e)?u(Ts(e,t,n,r,o)):u(_m(e,t,n,r),o)}function Um(e,t,n,r,o){return{[e]:At(A(e),t,n,r,_(o))}}function Nm(e,t,n,r,o){return e.reduce((s,a)=>({...s,...Um(a,t,n,r,o)}),{})}function Km(e,t,n,r,o){return Nm(e.keys,t,n,r,o)}function Ts(e,t,n,r,o){let s=Km(e,t,n,r,o);return T(s)}function bs(e,t){return wt(it(e),t)}function Lm(e,t){let n=e.filter(r=>ze(r,t)===m.False);return n.length===1?n[0]:w(n)}function wt(e,t,n={}){return me(e)?u(bs(e,t),n):U(e)?u(As(e,t),n):u(x(e)?Lm(e.anyOf,t):ze(e,t)!==m.False?S():e,n)}function vm(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(e))n[r]=wt(e[r],t);return n}function jm(e,t){return vm(e.properties,t)}function As(e,t){let n=jm(e,t);return T(n)}function ws(e,t){return Ot(it(e),t)}function Gm(e,t){let n=e.filter(r=>ze(r,t)!==m.False);return n.length===1?n[0]:w(n)}function Ot(e,t,n){return me(e)?u(ws(e,t),n):U(e)?u(Os(e,t),n):u(x(e)?Gm(e.anyOf,t):ze(e,t)!==m.False?e:S(),n)}function Dm(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(e))n[r]=Ot(e[r],t);return n}function Vm(e,t){return Dm(e.properties,t)}function Os(e,t){let n=Vm(e,t);return T(n)}function Rs(e,t){return be(e)?u(e.returns,t):S(t)}function yn(e){return ne(J(e))}function at(e,t,n){return u({[d]:"Record",type:"object",patternProperties:{[e]:t}},n)}function br(e,t,n){let r={};for(let o of e)r[o]=t;return P(r,{...n,[le]:"Record"})}function Bm(e,t,n){return Do(e)?br(te(e),t,n):at(e.pattern,t,n)}function Hm(e,t,n){return br(te(w(e)),t,n)}function Wm(e,t,n){return br([e.toString()],t,n)}function qm(e,t,n){return at(e.source,t,n)}function zm(e,t,n){let r=j(e.pattern)?Be:e.pattern;return at(r,t,n)}function Ym(e,t,n){return at(Be,t,n)}function Xm(e,t,n){return at(_o,t,n)}function Jm(e,t,n){return P({true:t,false:t},n)}function Qm(e,t,n){return at(Ve,t,n)}function Zm(e,t,n){return at(Ve,t,n)}function xn(e,t,n={}){return x(e)?Hm(e.anyOf,t,n):me(e)?Bm(e,t,n):ae(e)?Wm(e.const,t,n):Ke(e)?Jm(e,t,n):we(e)?Qm(e,t,n):Oe(e)?Zm(e,t,n):tr(e)?qm(e,t,n):Le(e)?zm(e,t,n):Zn(e)?Ym(e,t,n):Ge(e)?Xm(e,t,n):S(n)}function Sn(e){return globalThis.Object.getOwnPropertyNames(e.patternProperties)[0]}function Cs(e){let t=Sn(e);return t===Be?Re():t===Ve?fe():Re({pattern:t})}function Tn(e){return e.patternProperties[Sn(e)]}function ed(e,t){return t.parameters=Ht(e,t.parameters),t.returns=Ce(e,t.returns),t}function td(e,t){return t.parameters=Ht(e,t.parameters),t.returns=Ce(e,t.returns),t}function nd(e,t){return t.allOf=Ht(e,t.allOf),t}function rd(e,t){return t.anyOf=Ht(e,t.anyOf),t}function od(e,t){return j(t.items)||(t.items=Ht(e,t.items)),t}function sd(e,t){return t.items=Ce(e,t.items),t}function id(e,t){return t.items=Ce(e,t.items),t}function ad(e,t){return t.items=Ce(e,t.items),t}function ud(e,t){return t.item=Ce(e,t.item),t}function md(e,t){let n=ld(e,t.properties);return{...t,...P(n)}}function dd(e,t){let n=Ce(e,Cs(t)),r=Ce(e,Tn(t)),o=xn(n,r);return{...t,...o}}function pd(e,t){return t.index in e?e[t.index]:qe()}function cd(e,t){let n=ct(t),r=ee(t),o=Ce(e,t);return n&&r?yn(o):n&&!r?ne(o):!n&&r?J(o):o}function ld(e,t){return globalThis.Object.getOwnPropertyNames(t).reduce((n,r)=>({...n,[r]:cd(e,t[r])}),{})}function Ht(e,t){return t.map(n=>Ce(e,n))}function Ce(e,t){return be(t)?ed(e,t):Ae(t)?td(e,t):N(t)?nd(e,t):x(t)?rd(e,t):de(t)?od(e,t):Se(t)?sd(e,t):et(t)?id(e,t):nt(t)?ad(e,t):rt(t)?ud(e,t):G(t)?md(e,t):ot(t)?dd(e,t):er(t)?pd(e,t):t}function Ps(e,t){return Ce(t,pt(e))}function Ms(e){return u({[d]:"Integer",type:"integer"},e)}function fd(e,t,n){return{[e]:Pe(A(e),t,_(n))}}function gd(e,t,n){return e.reduce((o,s)=>({...o,...fd(s,t,n)}),{})}function Id(e,t,n){return gd(e.keys,t,n)}function Fs(e,t,n){let r=Id(e,t,n);return T(r)}function hd(e){let[t,n]=[e.slice(0,1),e.slice(1)];return[t.toLowerCase(),n].join("")}function yd(e){let[t,n]=[e.slice(0,1),e.slice(1)];return[t.toUpperCase(),n].join("")}function xd(e){return e.toUpperCase()}function Sd(e){return e.toLowerCase()}function Td(e,t,n){let r=ht(e.pattern);if(!st(r))return{...e,pattern:Es(e.pattern,t)};let a=[...Gt(r)].map(g=>A(g)),p=ks(a,t),l=w(p);return an([l],n)}function Es(e,t){return typeof e=="string"?t==="Uncapitalize"?hd(e):t==="Capitalize"?yd(e):t==="Uppercase"?xd(e):t==="Lowercase"?Sd(e):e:e.toString()}function ks(e,t){return e.map(n=>Pe(n,t))}function Pe(e,t,n={}){return ue(e)?Fs(e,t,n):me(e)?Td(e,t,n):x(e)?w(ks(e.anyOf,t),n):ae(e)?A(Es(e.const,t),n):u(e,n)}function $s(e,t={}){return Pe(e,"Capitalize",t)}function _s(e,t={}){return Pe(e,"Lowercase",t)}function Us(e,t={}){return Pe(e,"Uncapitalize",t)}function Ns(e,t={}){return Pe(e,"Uppercase",t)}function bd(e,t,n){let r={};for(let o of globalThis.Object.getOwnPropertyNames(e))r[o]=Ye(e[o],t,_(n));return r}function Ad(e,t,n){return bd(e.properties,t,n)}function Ks(e,t,n){let r=Ad(e,t,n);return T(r)}function wd(e,t){return e.map(n=>Ar(n,t))}function Od(e,t){return e.map(n=>Ar(n,t))}function Rd(e,t){let{[t]:n,...r}=e;return r}function Cd(e,t){return t.reduce((n,r)=>Rd(n,r),e)}function Pd(e,t,n){let r=K(e,[D,"$id","required","properties"]),o=Cd(n,t);return P(o,r)}function Md(e){let t=e.reduce((n,r)=>tn(r)?[...n,A(r)]:n,[]);return w(t)}function Ar(e,t){return N(e)?Q(wd(e.allOf,t)):x(e)?w(Od(e.anyOf,t)):G(e)?Pd(e,t,e.properties):P({})}function Ye(e,t,n){let r=v(t)?Md(t):t,o=pe(t)?te(t):t,s=L(e),a=L(t);return U(e)?Ks(e,o,n):ue(t)?Ls(e,t,n):s&&a?k("Omit",[e,r],n):!s&&a?k("Omit",[e,r],n):s&&!a?k("Omit",[e,r],n):u({...Ar(e,o),...n})}function Fd(e,t,n){return{[t]:Ye(e,[t],_(n))}}function Ed(e,t,n){return t.reduce((r,o)=>({...r,...Fd(e,o,n)}),{})}function kd(e,t,n){return Ed(e,t.keys,n)}function Ls(e,t,n){let r=kd(e,t,n);return T(r)}function $d(e,t,n){let r={};for(let o of globalThis.Object.getOwnPropertyNames(e))r[o]=Xe(e[o],t,_(n));return r}function _d(e,t,n){return $d(e.properties,t,n)}function vs(e,t,n){let r=_d(e,t,n);return T(r)}function Ud(e,t){return e.map(n=>wr(n,t))}function Nd(e,t){return e.map(n=>wr(n,t))}function Kd(e,t){let n={};for(let r of t)r in e&&(n[r]=e[r]);return n}function Ld(e,t,n){let r=K(e,[D,"$id","required","properties"]),o=Kd(n,t);return P(o,r)}function vd(e){let t=e.reduce((n,r)=>tn(r)?[...n,A(r)]:n,[]);return w(t)}function wr(e,t){return N(e)?Q(Ud(e.allOf,t)):x(e)?w(Nd(e.anyOf,t)):G(e)?Ld(e,t,e.properties):P({})}function Xe(e,t,n){let r=v(t)?vd(t):t,o=pe(t)?te(t):t,s=L(e),a=L(t);return U(e)?vs(e,o,n):ue(t)?js(e,t,n):s&&a?k("Pick",[e,r],n):!s&&a?k("Pick",[e,r],n):s&&!a?k("Pick",[e,r],n):u({...wr(e,o),...n})}function jd(e,t,n){return{[t]:Xe(e,[t],_(n))}}function Gd(e,t,n){return t.reduce((r,o)=>({...r,...jd(e,o,n)}),{})}function Dd(e,t,n){return Gd(e,t.keys,n)}function js(e,t,n){let r=Dd(e,t,n);return T(r)}function Vd(e,t){return k("Partial",[k(e,t)])}function Bd(e){return k("Partial",[$e(e)])}function Hd(e){let t={};for(let n of globalThis.Object.getOwnPropertyNames(e))t[n]=J(e[n]);return t}function Wd(e,t){let n=K(e,[D,"$id","required","properties"]),r=Hd(t);return P(r,n)}function Gs(e){return e.map(t=>Ds(t))}function Ds(e){return Te(e)?Vd(e.target,e.parameters):L(e)?Bd(e.$ref):N(e)?Q(Gs(e.allOf)):x(e)?w(Gs(e.anyOf)):G(e)?Wd(e,e.properties):tt(e)||Ke(e)||we(e)||ae(e)||$t(e)||Oe(e)||Le(e)||_t(e)||Ut(e)?e:P({})}function Rt(e,t){return U(e)?Vs(e,t):u({...Ds(e),...t})}function qd(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(e))n[r]=Rt(e[r],_(t));return n}function zd(e,t){return qd(e.properties,t)}function Vs(e,t){let n=zd(e,t);return T(n)}function Yd(e,t){return k("Required",[k(e,t)])}function Xd(e){return k("Required",[$e(e)])}function Jd(e){let t={};for(let n of globalThis.Object.getOwnPropertyNames(e))t[n]=K(e[n],[Y]);return t}function Qd(e,t){let n=K(e,[D,"$id","required","properties"]),r=Jd(t);return P(r,n)}function Bs(e){return e.map(t=>Hs(t))}function Hs(e){return Te(e)?Yd(e.target,e.parameters):L(e)?Xd(e.$ref):N(e)?Q(Bs(e.allOf)):x(e)?w(Bs(e.anyOf)):G(e)?Qd(e,e.properties):tt(e)||Ke(e)||we(e)||ae(e)||$t(e)||Oe(e)||Le(e)||_t(e)||Ut(e)?e:P({})}function Ct(e,t){return U(e)?Ws(e,t):u({...Hs(e),...t})}function Zd(e,t){let n={};for(let r of globalThis.Object.getOwnPropertyNames(e))n[r]=Ct(e[r],t);return n}function ep(e,t){return Zd(e.properties,t)}function Ws(e,t){let n=ep(e,t);return T(n)}function tp(e,t){return t.map(n=>L(n)?Or(e,n.$ref):ce(e,n))}function Or(e,t){return t in e?L(e[t])?Or(e,e[t].$ref):ce(e,e[t]):S()}function np(e){return St(e[0])}function rp(e){return We(e[0],e[1])}function op(e){return Tt(e[0])}function sp(e){return Rt(e[0])}function ip(e){return Ye(e[0],e[1])}function ap(e){return Xe(e[0],e[1])}function up(e){return Ct(e[0])}function mp(e,t,n){let r=tp(e,n);return t==="Awaited"?np(r):t==="Index"?rp(r):t==="KeyOf"?op(r):t==="Partial"?sp(r):t==="Omit"?ip(r):t==="Pick"?ap(r):t==="Required"?up(r):S()}function dp(e,t){return lt(ce(e,t))}function pp(e,t){return ft(ce(e,t))}function cp(e,t,n){return gt(Wt(e,t),ce(e,n))}function lp(e,t,n){return Ee(Wt(e,t),ce(e,n))}function fp(e,t){return Q(Wt(e,t))}function gp(e,t){return xt(ce(e,t))}function Ip(e,t){return P(globalThis.Object.keys(t).reduce((n,r)=>({...n,[r]:ce(e,t[r])}),{}))}function hp(e,t){let[n,r]=[ce(e,Tn(t)),Sn(t)],o=pt(t);return o.patternProperties[r]=n,o}function yp(e,t){return L(t)?{...Or(e,t.$ref),[D]:t[D]}:t}function xp(e,t){return ge(Wt(e,t))}function Sp(e,t){return w(Wt(e,t))}function Wt(e,t){return t.map(n=>ce(e,n))}function ce(e,t){return ee(t)?u(ce(e,K(t,[Y])),t):ct(t)?u(ce(e,K(t,[xe])),t):De(t)?u(yp(e,t),t):Se(t)?u(dp(e,t.items),t):et(t)?u(pp(e,t.items),t):Te(t)?u(mp(e,t.target,t.parameters)):be(t)?u(cp(e,t.parameters,t.returns),t):Ae(t)?u(lp(e,t.parameters,t.returns),t):N(t)?u(fp(e,t.allOf),t):nt(t)?u(gp(e,t.items),t):G(t)?u(Ip(e,t.properties),t):ot(t)?u(hp(e,t)):de(t)?u(xp(e,t.items||[]),t):x(t)?u(Sp(e,t.anyOf),t):t}function Tp(e,t){return t in e?ce(e,e[t]):S()}function qs(e){return globalThis.Object.getOwnPropertyNames(e).reduce((t,n)=>({...t,[n]:Tp(e,n)}),{})}var Rr=class{constructor(t){let n=qs(t),r=this.WithIdentifiers(n);this.$defs=r}Import(t,n){let r={...this.$defs,[t]:u(this.$defs[t],n)};return u({[d]:"Import",$defs:r,$ref:t})}WithIdentifiers(t){return globalThis.Object.getOwnPropertyNames(t).reduce((n,r)=>({...n,[r]:{...t[r],$id:r}}),{})}};function zs(e){return new Rr(e)}function Ys(e,t){return u({[d]:"Not",not:e},t)}function Xs(e,t){return Ae(e)?ge(e.parameters,t):S()}var bp=0;function Js(e,t={}){j(t.$id)&&(t.$id=`T${bp++}`);let n=pt(e({[d]:"This",$ref:`${t.$id}`}));return n.$id=t.$id,u({[le]:"Recursive",...n},t)}function Qs(e,t){let n=F(e)?new globalThis.RegExp(e):e;return u({[d]:"RegExp",type:"RegExp",source:n.source,flags:n.flags},t)}function Ap(e){return N(e)?e.allOf:x(e)?e.anyOf:de(e)?e.items??[]:[]}function Zs(e){return Ap(e)}function ei(e,t){return Ae(e)?u(e.returns,t):S(t)}var Cr=class{constructor(t){this.schema=t}Decode(t){return new Pr(this.schema,t)}},Pr=class{constructor(t,n){this.schema=t,this.decode=n}EncodeTransform(t,n){let s={Encode:a=>n[D].Encode(t(a)),Decode:a=>this.decode(n[D].Decode(a))};return{...n,[D]:s}}EncodeSchema(t,n){let r={Decode:this.decode,Encode:t};return{...n,[D]:r}}Encode(t){return De(this.schema)?this.EncodeTransform(t,this.schema):this.EncodeSchema(t,this.schema)}};function ti(e){return new Cr(e)}function ni(e={}){return u({[d]:e[d]??"Unsafe"},e)}function ri(e){return u({[d]:"Void",type:"void"},e)}var Mr={};qn(Mr,{Any:()=>He,Argument:()=>vo,Array:()=>lt,AsyncIterator:()=>ft,Awaited:()=>St,BigInt:()=>yt,Boolean:()=>sn,Capitalize:()=>$s,Composite:()=>ss,Const:()=>is,Constructor:()=>gt,ConstructorParameters:()=>as,Date:()=>dn,Enum:()=>us,Exclude:()=>wt,Extends:()=>At,Extract:()=>Ot,Function:()=>Ee,Index:()=>We,InstanceType:()=>Rs,Instantiate:()=>Ps,Integer:()=>Ms,Intersect:()=>Q,Iterator:()=>xt,KeyOf:()=>Tt,Literal:()=>A,Lowercase:()=>_s,Mapped:()=>Zo,Module:()=>zs,Never:()=>S,Not:()=>Ys,Null:()=>pn,Number:()=>fe,Object:()=>P,Omit:()=>Ye,Optional:()=>J,Parameters:()=>Xs,Partial:()=>Rt,Pick:()=>Xe,Promise:()=>mn,Readonly:()=>ne,ReadonlyOptional:()=>yn,Record:()=>xn,Recursive:()=>Js,Ref:()=>$e,RegExp:()=>Qs,Required:()=>Ct,Rest:()=>Zs,ReturnType:()=>ei,String:()=>Re,Symbol:()=>cn,TemplateLiteral:()=>an,Transform:()=>ti,Tuple:()=>ge,Uint8Array:()=>fn,Uncapitalize:()=>Us,Undefined:()=>ln,Union:()=>w,Unknown:()=>qe,Unsafe:()=>ni,Uppercase:()=>Ns,Void:()=>ri});var c=Mr;var f=null,wp=null;function Fr(e){f=e}function Er(e){wp=e}import{readFileSync as Op}from"fs";import{homedir as Rp}from"os";import{join as Cp}from"path";var qt;function oi(){if(qt!==void 0)return qt;try{let e=Op(Cp(Rp(),".claude.json"),"utf-8");qt=JSON.parse(e).mcpServers??{}}catch{qt={}}return qt}var y={maxSessions:5,idleTimeoutMinutes:15,sessionGcAgeMinutes:1440,maxPersistedSessions:50,maxAutoResponds:10,permissionMode:"plan",planApproval:"delegate",reasoningEffort:"medium"};function si(e){y={maxSessions:e.maxSessions??5,defaultModel:e.defaultModel,model:e.model,reasoningEffort:e.reasoningEffort??"medium",defaultWorkdir:e.defaultWorkdir,idleTimeoutMinutes:e.idleTimeoutMinutes??15,sessionGcAgeMinutes:e.sessionGcAgeMinutes??1440,maxPersistedSessions:e.maxPersistedSessions??50,fallbackChannel:e.fallbackChannel,agentChannels:e.agentChannels,maxAutoResponds:e.maxAutoResponds??10,permissionMode:e.permissionMode??"plan",planApproval:e.planApproval??"delegate",defaultHarness:e.defaultHarness}}function ii(e){if(e.messageChannel){let t=e.messageChannel.split("|");if(t.length>=3)return e.messageChannel;if(e.agentAccountId&&t.length>=2)return`${t[0]}|${e.agentAccountId}|${t.slice(1).join("|")}`}if(e.workspaceDir){let t=zt(e.workspaceDir);if(t)return t}if(e.messageChannel&&e.messageChannel.includes("|"))return e.messageChannel}function Pt(e,t){return t&&String(t).includes("|")?String(t):e?.channelId&&String(e.channelId).includes("|")?String(e.channelId):e?.channel&&e?.chatId?`${e.channel}|${e.chatId}`:e?.channel&&e?.senderId?`${e.channel}|${e.senderId}`:e?.id&&/^-?\d+$/.test(String(e.id))?`telegram|${e.id}`:y.fallbackChannel??"unknown"}function bn(e){return e?.messageThreadId??void 0}function zt(e){let t=y.agentChannels;if(!t)return;let n=s=>s.replace(/\/+$/,""),r=n(e),o=Object.entries(t).sort((s,a)=>a[0].length-s[0].length);for(let[s,a]of o)if(r===n(s)||r.startsWith(n(s)+"/"))return a}function ai(e){if(!e)return;let t=e.match(/:topic:(\d+)$/);return t?parseInt(t[1],10):void 0}function Mp(e){return e instanceof Error?e.message:String(e)}function Fp(e){return!e||typeof e!="object"?!1:typeof e.prompt=="string"}function ui(e){return{name:"agent_launch",description:"Launch a coding agent session in background to execute a development task. Sessions are multi-turn by default \u2014 they stay open for follow-up messages via agent_respond. Set multi_turn_disabled: true for fire-and-forget sessions. Supports resuming previous sessions. Returns a session ID and name for tracking.",parameters:c.Object({prompt:c.String({description:"The task prompt to execute"}),name:c.Optional(c.String({description:"Short human-readable name for the session (kebab-case, e.g. 'fix-auth'). Auto-generated from prompt if omitted."})),workdir:c.Optional(c.String({description:"Working directory (defaults to cwd)"})),model:c.Optional(c.String({description:"Model name to use"})),system_prompt:c.Optional(c.String({description:"Additional system prompt"})),allowed_tools:c.Optional(c.Array(c.String(),{description:"List of allowed tools"})),resume_session_id:c.Optional(c.String({description:"Session ID to resume (from a previous session's harnessSessionId). Continues the conversation from where it left off."})),fork_session:c.Optional(c.Boolean({description:"When resuming, fork to a new session instead of continuing the existing one. Use with resume_session_id."})),multi_turn_disabled:c.Optional(c.Boolean({description:"Disable multi-turn mode. By default sessions stay open for follow-up messages. Set to true for fire-and-forget sessions."})),notify_on_turn_end:c.Optional(c.Boolean({description:"Send wake notifications at every turn end. Defaults to true."})),permission_mode:c.Optional(c.Union([c.Literal("default"),c.Literal("plan"),c.Literal("acceptEdits"),c.Literal("bypassPermissions")],{description:"Permission mode for the session. Defaults to plugin config (plan by default)."})),harness:c.Optional(c.String({description:"Agent harness to use (e.g. 'claude-code'). Defaults to 'claude-code'."}))}),async execute(t,n){if(!f)return{content:[{type:"text",text:"Error: SessionManager not initialized. The code-agent service must be running."}]};if(!Fp(n))return{content:[{type:"text",text:"Error: Invalid parameters. Expected at least { prompt }."}]};n.agentId&&console.warn(`[agent_launch] \u26A0\uFE0F agentId="${n.agentId}" was passed as a parameter \u2014 this is WRONG. agentId is only for sessions_spawn (OpenClaw sub-agents), not agent_launch (CC sessions). The field is being ignored. ctx.agentId="${e.agentId}" will be used for origin routing instead.`);let r=n.workdir||e.workspaceDir||y.defaultWorkdir||process.cwd();if(!Pp(r))return{content:[{type:"text",text:`Error: Working directory does not exist: ${r}`}]};try{let o=n.harness??y.defaultHarness,s=o==="codex"?y.model??y.defaultModel:y.defaultModel,a=n.resume_session_id;if(a){let oe=f.resolveHarnessSessionId(a);if(!oe)return{content:[{type:"text",text:`Error: Could not resolve resume_session_id "${a}" to a session ID. Use agent_sessions to list available sessions.`}]};a=oe}let p=ii(e),l=Pt({id:t},p||zt(r)),g=e.sessionKey||void 0;!g&&e.agentId&&console.warn(`[agent_launch] ctx.sessionKey is not populated. ctx fields: agentId=${e.agentId}, messageChannel=${e.messageChannel}, agentAccountId=${e.agentAccountId}, workspaceDir=${e.workspaceDir}`);let b=f.spawn({prompt:n.prompt,name:n.name,workdir:r,model:n.model??s,reasoningEffort:y.reasoningEffort,systemPrompt:n.system_prompt,allowedTools:n.allowed_tools,resumeSessionId:a,forkSession:n.fork_session,multiTurn:!n.multi_turn_disabled,notifyOnTurnEnd:n.notify_on_turn_end??!0,permissionMode:n.permission_mode,originChannel:l,originThreadId:ai(g),originAgentId:e.agentId||void 0,originSessionKey:g,harness:o}),q=n.prompt.length>80?n.prompt.slice(0,80)+"...":n.prompt,O=["Session launched successfully.",` Name: ${b.name}`,` ID: ${b.id}`,` Dir: ${r}`,` Model: ${b.model??"default"}`,` Prompt: "${q}"`];return n.resume_session_id&&O.push(` Resume: ${n.resume_session_id}${n.fork_session?" (forked)":""}`),O.push(n.multi_turn_disabled?" Mode: single-turn (fire-and-forget)":" Mode: multi-turn (use agent_respond to send follow-up messages)"),O.push("","Use agent_sessions to check status, agent_output to see output."),{content:[{type:"text",text:O.join(`
|
|
2
|
+
`)}]}}catch(o){let s=Mp(o),a=s.includes("Max sessions")?"":`
|
|
3
|
+
|
|
4
|
+
Use agent_sessions to see active sessions and their status.`;return{content:[{type:"text",text:`Error launching session: ${s}${a}`}]}}}}}import{existsSync as $p,readFileSync as _p}from"fs";function _e(e){let t=Math.floor(e/1e3),n=Math.floor(t/60),r=t%60;return n>0?`${n}m${r}s`:`${r}s`}var Ep=new Set(["a","an","the","is","are","was","were","be","been","being","have","has","had","do","does","did","will","would","could","should","may","might","shall","can","need","must","i","me","my","we","our","you","your","it","its","he","she","to","of","in","for","on","with","at","by","from","as","into","through","about","that","this","these","those","and","or","but","if","then","so","not","no","please","just","also","very","all","some","any","each","make","write","create","build","implement","add","update"]);function mi(e){let n=e.toLowerCase().replace(/[^a-z0-9\s-]/g," ").split(/\s+/).filter(r=>r.length>1&&!Ep.has(r)).slice(0,3);return n.length===0?"session":n.join("-")}var kp={starting:"\u{1F7E1}",running:"\u{1F7E2}",completed:"\u2705",failed:"\u274C",killed:"\u26D4"};function di(e){let t=kp[e.status]??"\u2753",n=_e(e.duration),r=e.multiTurn?"multi-turn":"single",o=e.prompt.length>80?e.prompt.slice(0,80)+"...":e.prompt,s=e.costUsd>0?` | $${e.costUsd.toFixed(2)}`:"",a=[`${t} ${e.name} [${e.id}] (${n}${s}) \u2014 ${r}`,` \u{1F4C1} ${e.workdir}`,` \u{1F4DD} "${o}"`];return e.phase!==e.status&&a.push(` \u2699\uFE0F Phase: ${e.phase}`),e.harnessSessionId&&a.push(` \u{1F517} Session ID: ${e.harnessSessionId}`),e.resumeSessionId&&a.push(` \u21A9\uFE0F Resumed from: ${e.resumeSessionId}${e.forkSession?" (forked)":""}`),a.join(`
|
|
5
|
+
`)}function An(e,t){let n=e.sessionsWithDuration>0?e.totalDurationMs/e.sessionsWithDuration:0,{completed:r,failed:o,killed:s}=e.sessionsByStatus,a=["\u{1F4CA} OpenClaw Code Agent Stats","","\u{1F4CB} Sessions",` Launched: ${e.totalLaunched}`,` Running: ${t}`,` Completed: ${r}`,` Failed: ${o}`,` Killed: ${s}`,"",`\u23F1\uFE0F Average duration: ${n>0?_e(n):"n/a"}`];if(e.mostExpensive){let p=e.mostExpensive;a.push("","\u{1F3C6} Notable session",` ${p.name} [${p.id}]`,` \u{1F4DD} "${p.prompt}"`)}return a.join(`
|
|
6
|
+
`)}function Je(e,t){return t<=0?"":e.length<=t?e:t<=3?".".repeat(t):e.slice(0,t-3)+"..."}function pi(e,t){let n=e.split(`
|
|
7
|
+
`),r=[],o=0;for(let s=n.length-1;s>=0;s--){let a=n[s].length+(r.length>0?1:0);if(o+a>t&&r.length>0)break;r.unshift(n[s]),o+=a}return r.join(`
|
|
8
|
+
`)}var Up=50,Np=1,Kp=new Set(["starting","running","completed","failed","killed"]),Lp=5,vp=1440*60*1e3;function jp(e){let t=Number(e);return!Number.isFinite(t)||t<Np?Up:Math.floor(t)}function Gp(e){return typeof e=="string"&&Kp.has(e)}function Dp(e){let t=_e(e.duration),n=` | Cost: $${e.costUsd.toFixed(4)}`,r=e.status==="running"?` | Phase: ${e.phase}`:"";return[`Session: ${e.name} [${e.id}] | Status: ${e.status.toUpperCase()}${r}${n} | Duration: ${t}`,`${"\u2500".repeat(60)}`].join(`
|
|
9
|
+
`)}function Vp(e){return[`Session: ${e.name||e.harnessSessionId} | Status: ${e.status.toUpperCase()} | Cost: $${e.costUsd.toFixed(4)}`,`(retrieved from ${e.outputPath} \u2014 evicted from runtime cache \u2014 showing persisted output)`,`${"\u2500".repeat(60)}`].join(`
|
|
10
|
+
`)}function Bp(e){let t=[];return e.error&&t.push(`Error: ${e.error}`),e.result?.result&&t.push(`Result: ${e.result.result}`),e.result&&t.push(`Result status: ${e.result.subtype}`),t.length>0?`
|
|
11
|
+
(no output yet)
|
|
12
|
+
${t.join(`
|
|
13
|
+
`)}`:`
|
|
14
|
+
(no output yet)`}function wn(e,t,n={}){let r=jp(n.lines),o=e.resolve(t);if(!o){let p=e.getPersistedSession(t);if(p?.outputPath&&$p(p.outputPath))try{let l=_p(p.outputPath,"utf-8"),g=l;!n.full&&l&&(g=l.split(`
|
|
15
|
+
`).slice(-r).join(`
|
|
16
|
+
`));let b=Vp(p);return g?`${b}
|
|
17
|
+
${g}`:`${b}
|
|
18
|
+
(output file was empty)`}catch(l){let g=l instanceof Error?l.message:String(l);return`Error: Session "${t}" was cleaned up (expired) and output file could not be read: ${g}`}return`Error: Session "${t}" not found.`}let s=n.full?o.getOutput():o.getOutput(r),a=Dp(o);return s.length===0?`${a}${Bp(o)}`:`${a}
|
|
19
|
+
${s.join(`
|
|
20
|
+
`)}`}function On(e,t="all",n,r={}){let o=e.listPersistedSessions()??[],a=Hp(e.list("all"),o);if(t!=="all"&&(a=a.filter(p=>p.status===t)),n&&(a=a.filter(p=>p.originChannel===n)),r.full){let p=Date.now()-vp;a=a.filter(l=>(l.startedAt??0)>=p)}else a=a.slice(0,Lp);return a.length===0?"No sessions found.":a.map(p=>di(p)).join(`
|
|
21
|
+
|
|
22
|
+
`)}function Hp(e,t){let n=new Map;for(let r of t){if(!Gp(r.status))continue;let o=r.completedAt??Date.now(),s=r.createdAt??o,a=r.sessionId??`persisted:${r.harnessSessionId}`;n.set(a,{id:r.sessionId??r.harnessSessionId,name:r.name||r.harnessSessionId,status:r.status,startedAt:r.createdAt??0,completedAt:r.completedAt,duration:Math.max(0,o-s),prompt:r.prompt??"",workdir:r.workdir??"(unknown)",costUsd:r.costUsd??0,multiTurn:!0,phase:r.status,harnessSessionId:r.harnessSessionId,originChannel:r.originChannel,originThreadId:r.originThreadId})}for(let r of e)n.set(r.id,{id:r.id,name:r.name,status:r.status,startedAt:r.startedAt,completedAt:r.completedAt,duration:r.duration,prompt:r.prompt,workdir:r.workdir,costUsd:r.costUsd,multiTurn:r.multiTurn,phase:r.phase,harnessSessionId:r.harnessSessionId,originChannel:r.originChannel,originThreadId:r.originThreadId});return[...n.values()].sort((r,o)=>(o.startedAt??0)-(r.startedAt??0))}function Wp(e){if(!e||typeof e!="object")return"all";let t=e.status;switch(t){case"running":case"completed":case"failed":case"killed":case"all":return t;default:return"all"}}function ci(e){return{name:"agent_sessions",description:"List coding agent sessions with their status and progress. By default, shows the 5 most recent sessions; set `full` to show all sessions from the last 24 hours.",parameters:c.Object({status:c.Optional(c.Union([c.Literal("all"),c.Literal("running"),c.Literal("completed"),c.Literal("failed"),c.Literal("killed")],{description:'Filter by status (default "all")'})),full:c.Optional(c.Boolean({description:"Show all sessions from the last 24h instead of just the most recent 5"}))}),async execute(t,n){if(!f)return{content:[{type:"text",text:"Error: SessionManager not initialized. The code-agent service must be running."}]};let r=Wp(n),o=e?.workspaceDir?zt(e.workspaceDir):void 0,s=!!(n&&typeof n=="object"&&n.full===!0);return{content:[{type:"text",text:On(f,r,o,{full:s})}]}}}}function Rn(e,t,n){let r=e.resolve(t);return r?r.status==="completed"||r.status==="failed"||r.status==="killed"?`Session ${r.name} [${r.id}] is already ${r.status}. No action needed.`:n==="completed"?(r.complete(),`Session ${r.name} [${r.id}] marked as completed.`):(e.kill(r.id),`Session ${r.name} [${r.id}] has been terminated.`):`Error: Session "${t}" not found.`}function qp(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.session!="string"?!1:t.reason===void 0?!0:t.reason==="completed"||t.reason==="killed"}function li(e){return{name:"agent_kill",description:"Terminate or complete a running coding agent session by name or ID. Use reason='completed' to mark a session as successfully completed instead of killed.",parameters:c.Object({session:c.String({description:"Session name or ID to terminate"}),reason:c.Optional(c.Union([c.Literal("completed"),c.Literal("killed")],{description:"Reason for closing the session. 'completed' marks it as successfully done (sends \u2705 notification). 'killed' (default) terminates it."}))}),async execute(t,n){return f?qp(n)?{content:[{type:"text",text:Rn(f,n.session,n.reason)}]}:{content:[{type:"text",text:"Error: Invalid parameters. Expected { session, reason? }."}]}:{content:[{type:"text",text:"Error: SessionManager not initialized. The code-agent service must be running."}]}}}}function zp(e){return!e||typeof e!="object"?!1:typeof e.session=="string"}function fi(e){return{name:"agent_output",description:"Show recent output from a coding agent session (by name or ID).",parameters:c.Object({session:c.String({description:"Session name or ID to get output from"}),lines:c.Optional(c.Number({description:"Number of recent lines to show (default 50)"})),full:c.Optional(c.Boolean({description:"Show all available output"}))}),async execute(t,n){return f?zp(n)?{content:[{type:"text",text:wn(f,n.session,{full:n.full,lines:n.lines})}]}:{content:[{type:"text",text:"Error: Invalid parameters. Expected { session, lines?, full? }."}]}:{content:[{type:"text",text:"Error: SessionManager not initialized. The code-agent service must be running."}]}}}}var Yp=new Set(["killed","completed","failed"]),Xp=new Set(["idle-timeout","shutdown","done"]),Jp=10,Qp=100,Zp=/\b(change|swap|replace|remove|add|update|instead|don't|revise|modify)\b/i;function Yt(e){return e instanceof Error?e.message:String(e)}function ec(e){switch(e){case"completed":return"completed";case"failed":return"failed";default:return"idle-kill"}}function gi(e){return"id"in e?e.id:e.sessionId??e.harnessSessionId}function tc(e){return e.status==="killed"&&e.completedAt==null}function nc(e,t){return Yp.has(e.status)&&!!e.harnessSessionId&&(e.status==="failed"||e.status==="completed"&&e.killReason==="done"||e.status==="killed"&&(Xp.has(e.killReason??"")||t&&tc(e)))}function rc(e,t){if(!(t.trim().length<Qp&&!Zp.test(t)))return{text:["Cannot approve and revise in the same call.","Your message appears to contain revision feedback. Send it first WITHOUT approve=true:",` agent_respond(session='${e}', message='<your feedback>')`,"The agent will revise the plan. Then approve the revised plan."].join(`
|
|
23
|
+
`),isError:!0}}async function oc(e,t,n,r={}){if(nc(t,r.allowRecoveredRunningStub===!0))try{let o={prompt:n,workdir:t.workdir,name:t.name,model:t.model,reasoningEffort:t.reasoningEffort,resumeSessionId:t.harnessSessionId,multiTurn:!0,notifyOnTurnEnd:t.notifyOnTurnEnd,originChannel:t.originChannel,originThreadId:t.originThreadId,originAgentId:t.originAgentId,originSessionKey:t.originSessionKey,permissionMode:t.currentPermissionMode,harness:"harnessName"in t?t.harnessName:t.harness},s=e.spawn(o),a=ec(t.status);return e.deliverToTelegram(s,`\u{1F504} [${s.name}] Auto-resumed from ${a}`),{text:`Auto-resumed ${a} session ${s.name} [${s.id}]. Use agent_output to see the response.`}}catch(o){return{text:`Error auto-resuming session ${t.name} [${gi(t)}]: ${Yt(o)}`,isError:!0}}}function sc(e,t,n){let r=t.lobsterResumeToken;if(r){if(t.lobsterResumeToken=void 0,n.approve&&t.pendingPlanApproval)return e.resumeLobsterApproval(r,!0).catch(o=>{console.error(`[Respond] Lobster resume failed, falling back to direct mode switch: ${Yt(o)}`),t.switchPermissionMode("bypassPermissions"),t.sendMessage(n.message).catch(s=>{console.error(`[Respond] Fallback sendMessage also failed: ${Yt(s)}`)})}),{text:`Plan approved. Lobster workflow resuming for session ${t.name} [${t.id}].`};e.resumeLobsterApproval(r,!1).catch(o=>{console.error(`[Respond] Lobster cancel failed (non-critical): ${Yt(o)}`)})}}async function Cn(e,t){let n=e.resolve(t.session),r=n?void 0:e.getPersistedSession(t.session);if(!n&&!r)return{text:`Error: Session "${t.session}" not found.`,isError:!0};let s=await oc(e,n??r,t.message,{allowRecoveredRunningStub:!n});if(s)return s;if(!n)return{text:`Error: Session ${r.name} [${gi(r)}] is not running (status: ${r.status}). Cannot send a message to a non-running session.`,isError:!0};if(n.status!=="running")return{text:`Error: Session ${n.name} [${n.id}] is not running (status: ${n.status}). Cannot send a message to a non-running session.`,isError:!0};let a=y.maxAutoResponds??Jp;if(t.userInitiated)n.resetAutoRespond();else if(n.autoRespondCount>=a)return{text:`\u26A0\uFE0F Auto-respond limit reached (${n.autoRespondCount}/${a}). Ask the user to provide the answer for session ${n.name}. Then call agent_respond with their answer and set userInitiated: true to reset the counter.`};let p=sc(e,n,t);if(p)return p;try{t.interrupt&&await n.interrupt();let l="";if(t.approve&&n.pendingPlanApproval){let b=rc(n.name,t.message);if(b)return b;n.switchPermissionMode("bypassPermissions")}else t.approve?l=`
|
|
24
|
+
\u26A0\uFE0F approve=true was set but session has no pending plan approval.`:n.pendingPlanApproval&&(l=`
|
|
25
|
+
\u2139\uFE0F Session has a pending plan \u2014 sending as revision feedback. The agent will revise and re-submit. Set approve=true to approve instead.`);await n.sendMessage(t.message),t.userInitiated||n.incrementAutoRespond();let g=Je(t.message,80);return{text:[`Message sent to session ${n.name} [${n.id}].`,t.interrupt?" (interrupted current turn first)":"",` Message: "${g}"`,l,"Use agent_output to see the response."].filter(Boolean).join(`
|
|
26
|
+
`)}}catch(l){return{text:`Error sending message to session ${n.name} [${n.id}]: ${Yt(l)}`,isError:!0}}}function ic(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.session=="string"&&typeof t.message=="string"}function Ii(e){return{name:"agent_respond",description:"Send a follow-up message to a running coding agent session. The session must be running. Sessions are multi-turn by default, so this works with any session unless it was launched with multi_turn_disabled: true.",parameters:c.Object({session:c.String({description:"Session name or ID to respond to"}),message:c.String({description:"The message to send to the session"}),interrupt:c.Optional(c.Boolean({description:"If true, interrupt the current turn before sending the message. Useful to redirect the session mid-response."})),userInitiated:c.Optional(c.Boolean({description:"Set to true when the message comes from the user (not auto-generated). Resets the auto-respond counter and bypasses the auto-respond limit."})),approve:c.Optional(c.Boolean({description:"Set to true to approve a pending plan and switch the session from plan mode to bypassPermissions. Only works when the session has a pending plan approval (after ExitPlanMode / set_permission_mode). To request changes instead, omit this flag \u2014 the message will be sent as revision feedback and the agent will revise the plan."}))}),async execute(t,n){if(!f)return{content:[{type:"text",text:"Error: SessionManager not initialized. The code-agent service must be running."}]};if(!ic(n))return{content:[{type:"text",text:"Error: Invalid parameters. Expected { session, message, interrupt?, userInitiated?, approve? }."}]};let r=await Cn(f,n);return{isError:r.isError??!1,content:[{type:"text",text:r.text}]}}}}function hi(e){return{name:"agent_stats",description:"Show OpenClaw Code Agent usage metrics: session counts by status, average duration, and notable sessions.",parameters:c.Object({}),async execute(t,n){if(!f)return{content:[{type:"text",text:"Error: SessionManager not initialized. The code-agent service must be running."}]};let r=f.getMetrics(),o=f.list("running").length;return{content:[{type:"text",text:An(r,o)}]}}}}function ac(e){return e instanceof Error?e.message:String(e)}function yi(e){e.registerCommand({name:"agent",description:"Launch a coding agent session. Usage: /agent [--name <name>] <prompt>",acceptsArgs:!0,requireAuth:!0,handler:t=>{if(!f)return{text:"Error: SessionManager not initialized. The code-agent service must be running."};let n=(t.args??"").trim();if(!n)return{text:"Usage: /agent [--name <name>] <prompt>"};let r,o=n.match(/^--name\s+(\S+)\s+/);o&&(r=o[1],n=n.slice(o[0].length).trim());let s=n;if(!s)return{text:"Usage: /agent [--name <name>] <prompt>"};try{let a=y.defaultHarness,p=a==="codex"?y.model??y.defaultModel:y.defaultModel,l=f.spawn({prompt:s,name:r,workdir:y.defaultWorkdir||process.cwd(),model:p,reasoningEffort:y.reasoningEffort,originChannel:Pt(t),originThreadId:bn(t),harness:a}),g=s.length>80?s.slice(0,80)+"...":s;return{text:["Session launched.",` Name: ${l.name}`,` ID: ${l.id}`,` Prompt: "${g}"`,` Status: ${l.status}`].join(`
|
|
27
|
+
`)}}catch(a){let p=ac(a),l=p.includes("Max sessions")?"":`
|
|
28
|
+
|
|
29
|
+
Use /agent_sessions to see active sessions.`;return{text:`Error launching session: ${p}${l}`}}}})}function xi(e){e.registerCommand({name:"agent_sessions",description:"List coding agent sessions. Usage: /agent_sessions [--full]",acceptsArgs:!0,requireAuth:!0,handler:t=>{if(!f)return{text:"Error: SessionManager not initialized. The code-agent service must be running."};let n=(t.args??"").split(/\s+/).includes("--full");return{text:On(f,"all",void 0,{full:n})}}})}function Si(e){e.registerCommand({name:"agent_kill",description:"Kill a coding agent session by name or ID",acceptsArgs:!0,requireAuth:!0,handler:t=>{if(!f)return{text:"Error: SessionManager not initialized. The code-agent service must be running."};let n=t.args?.trim();return n?{text:Rn(f,n,"killed")}:{text:"Usage: /agent_kill <name-or-id>"}}})}function uc(e){return e instanceof Error?e.message:String(e)}function Ti(e){e.registerCommand({name:"agent_resume",description:"Resume a previous coding agent session. Usage: /agent_resume <id-or-name> [prompt] or /agent_resume --list to see resumable sessions.",acceptsArgs:!0,requireAuth:!0,handler:t=>{if(!f)return{text:"Error: SessionManager not initialized. The code-agent service must be running."};let n=(t.args??"").trim();if(!n)return{text:`Usage: /agent_resume <id-or-name> [prompt]
|
|
30
|
+
/agent_resume --list \u2014 list resumable sessions
|
|
31
|
+
/agent_resume --fork <id-or-name> [prompt] \u2014 fork instead of continuing`};if(n==="--list"){let b=f.listPersistedSessions();return b.length===0?{text:"No resumable sessions found. Sessions are persisted after completion."}:{text:`Resumable sessions:
|
|
32
|
+
|
|
33
|
+
${b.map(O=>{let oe=O.prompt.length>60?O.prompt.slice(0,60)+"...":O.prompt,Me=O.completedAt?`completed ${_e(Date.now()-O.completedAt)} ago`:O.status;return[` ${O.name} \u2014 ${Me}`,` Session ID: ${O.harnessSessionId}`,` \u{1F4C1} ${O.workdir}`,` \u{1F4DD} "${oe}"`].join(`
|
|
34
|
+
`)}).join(`
|
|
35
|
+
|
|
36
|
+
`)}`}}let r=!1;n.startsWith("--fork ")&&(r=!0,n=n.slice(7).trim());let o=n.indexOf(" "),s,a;o===-1?(s=n,a="Continue where you left off."):(s=n.slice(0,o),a=n.slice(o+1).trim()||"Continue where you left off.");let p=f.resolveHarnessSessionId(s);if(!p)return{text:`Error: Could not find a session ID for "${s}".
|
|
37
|
+
Use /agent_resume --list to see available sessions.`};let l=f.getPersistedSession(s),g=l?.workdir??process.cwd();try{let b=f.spawn({prompt:a,workdir:g,name:l?.name,model:l?.model,resumeSessionId:p,forkSession:r,originChannel:Pt(t),originThreadId:bn(t)??l?.originThreadId,originAgentId:t?.agentId??l?.originAgentId,originSessionKey:t?.sessionKey??l?.originSessionKey,harness:l?.harness}),q=a.length>80?a.slice(0,80)+"...":a;return{text:[`Session resumed${r?" (forked)":""}.`,` Name: ${b.name}`,` ID: ${b.id}`,` Resume from: ${p}`,` Dir: ${g}`,` Prompt: "${q}"`].join(`
|
|
38
|
+
`)}}catch(b){let q=uc(b),O=q.includes("Max sessions")?"":`
|
|
39
|
+
|
|
40
|
+
Use /agent_sessions to see active sessions or /agent_resume --list to see resumable sessions.`;return{text:`Error resuming session: ${q}${O}`}}}})}function bi(e){e.registerCommand({name:"agent_respond",description:"Send a follow-up message to a running coding agent session. Usage: /agent_respond <id-or-name> <message>",acceptsArgs:!0,requireAuth:!0,handler:async t=>{if(!f)return{text:"Error: SessionManager not initialized. The code-agent service must be running."};let n=(t.args??"").trim();if(!n)return{text:`Usage: /agent_respond <id-or-name> <message>
|
|
41
|
+
/agent_respond --interrupt <id-or-name> <message>`};let r=!1,o=n;o.startsWith("--interrupt ")&&(r=!0,o=o.slice(12).trim());let s=o.indexOf(" ");if(s===-1)return{text:"Error: Missing message. Usage: /agent_respond <id-or-name> <message>"};let a=o.slice(0,s),p=o.slice(s+1).trim();return p?{text:(await Cn(f,{session:a,message:p,interrupt:r,userInitiated:!0})).text}:{text:"Error: Empty message. Usage: /agent_respond <id-or-name> <message>"}}})}function Ai(e){e.registerCommand({name:"agent_stats",description:"Show OpenClaw Code Agent usage metrics",acceptsArgs:!1,requireAuth:!0,handler:()=>{if(!f)return{text:"Error: SessionManager not initialized. The code-agent service must be running."};let t=f.getMetrics(),n=f.list("running").length;return{text:An(t,n)}}})}var mc=50;function wi(e){e.registerCommand({name:"agent_output",description:"Show recent output from a coding agent session. Usage: /agent_output <id-or-name> [--full] [--lines N]",acceptsArgs:!0,requireAuth:!0,handler:t=>{if(!f)return{text:"Error: SessionManager not initialized. The code-agent service must be running."};let n=(t.args??"").trim();if(!n)return{text:"Usage: /agent_output <id-or-name> [--full] [--lines N]"};let r=n.split(/\s+/),o="",s=!1,a=mc;for(let l=0;l<r.length;l++)if(r[l]==="--full")s=!0;else if(r[l]==="--lines"&&l+1<r.length){let g=parseInt(r[l+1],10);!isNaN(g)&&g>0&&(a=g),l++}else o||(o=r[l]);return o?{text:wn(f,o,{full:s,lines:a})}:{text:"Usage: /agent_output <id-or-name> [--full] [--lines N]"}}})}import{execFile as Gi}from"child_process";import{existsSync as sl}from"fs";import{fileURLToPath as Di}from"url";import{dirname as il,join as Ur}from"path";import{EventEmitter as kc}from"events";import Ri from"crypto";var Oi="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var dc=128,ut,Mt,pc=e=>{!ut||ut.length<e?(ut=Buffer.allocUnsafe(e*dc),Ri.randomFillSync(ut),Mt=0):Mt+e>ut.length&&(Ri.randomFillSync(ut),Mt=0),Mt+=e};var Ci=(e=21)=>{pc(e|=0);let t="";for(let n=Mt-e;n<Mt;n++)t+=Oi[ut[n]&63];return t};import{query as cc}from"@anthropic-ai/claude-agent-sdk";var Pn=class{name="claude-code";supportedPermissionModes=["default","plan","acceptEdits","bypassPermissions"];questionToolNames=["AskUserQuestion"];planApprovalToolNames=["ExitPlanMode","set_permission_mode"];launch(t){let n={cwd:t.cwd,model:t.model,permissionMode:t.permissionMode,allowDangerouslySkipPermissions:!0,allowedTools:t.allowedTools,systemPrompt:t.systemPrompt,includePartialMessages:!0,abortController:t.abortController,mcpServers:t.mcpServers};t.resumeSessionId&&(n.resume=t.resumeSessionId,n.forkSession=t.forkSession??!1);let r=cc({prompt:t.prompt,options:n});return{messages:this.adaptMessages(r),async setPermissionMode(o){typeof r.setPermissionMode=="function"&&await r.setPermissionMode(o)},async streamInput(o){typeof r.streamInput=="function"&&await r.streamInput(o)},async interrupt(){typeof r.interrupt=="function"&&await r.interrupt()}}}buildUserMessage(t,n){return{type:"user",message:{role:"user",content:t},parent_tool_use_id:null,session_id:n}}async*adaptMessages(t){for await(let n of t){let r=n;if(r.type==="system"&&r.subtype==="init")yield{type:"init",session_id:r.session_id??""};else if(r.type==="system"&&r.subtype==="status"&&r.permissionMode)yield{type:"permission_mode_change",mode:r.permissionMode};else if(r.type==="assistant")for(let o of r.message?.content??[])o.type==="text"?yield{type:"text",text:o.text}:o.type==="tool_use"&&(yield{type:"tool_use",name:o.name,input:o.input});else r.type==="result"&&(yield{type:"result",data:{success:r.subtype==="success",duration_ms:r.duration_ms??0,total_cost_usd:r.total_cost_usd??0,num_turns:r.num_turns??0,result:r.result,session_id:r.session_id??""}})}}};import{parse as hc,resolve as yc}from"path";import{Codex as xc}from"@openai/codex-sdk";var lc=["proceed","continue","implement","apply","run","merge","deploy","commit"],fc=["shall i proceed","do you want me to","would you like me to","please confirm","should i continue","can i proceed","should i proceed","should i go ahead","want me to continue","approve and i'll","confirm and i'll"],gc=["why this failed was","why did this fail","what failed","what happened","how can i help","is this clear","any questions","anything else","let me know","would you like a summary"];function Ic(e){return e.toLowerCase().replace(/\s+/g," ").trim()}function Mn(e){let t=Ic(e);if(!t||gc.some(r=>t.includes(r)))return!1;let n=lc.some(r=>t.includes(r));return fc.some(r=>t.includes(r))?n||t.includes("confirm"):t.endsWith("?")?n:!1}var Pi=1e4,Mi="codex:waiting-for-user",Sc=1.1/1e6,Tc=.275/1e6,bc=4.4/1e6,Ac="OPENCLAW_CODEX_HEARTBEAT_MS",wc="OPENCLAW_CODEX_BYPASS_ADDITIONAL_DIRS";function Oc(e){if(!e)return 0;let t=e.cached_input_tokens??0,n=Math.max(0,(e.input_tokens??0)-t),r=e.output_tokens??0;return n*Sc+t*Tc+r*bc}function Fi(e,t={}){return{success:!1,duration_ms:0,total_cost_usd:0,num_turns:0,session_id:e,...t}}function Rc(e){if(typeof e=="string")return e;if(!e||typeof e!="object")return String(e);let t=e;return typeof t.message?.content=="string"?t.message.content:typeof t.text=="string"?t.text:String(e)}function Cc(e){return["[SYSTEM: First turn only. Do not implement yet.]","Start by producing a concise implementation plan only.","Then end your response with an explicit question asking whether you should proceed with implementation.","",e].join(`
|
|
42
|
+
`)}function Ei(e){return e instanceof Error?e.message:String(e)}function Pc(e){return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function Mc(e){return hc(yc(e)).root||"/"}function Fc(e){let n=[Mc(e)];return n.push(...Pc(process.env[wc])),[...new Set(n)]}function Ec(e,t){let r=(t??e.permissionMode)==="bypassPermissions"?Fc(e.cwd):void 0;return{model:e.model,modelReasoningEffort:e.reasoningEffort,workingDirectory:e.cwd,sandboxMode:"danger-full-access",approvalPolicy:"never",skipGitRepoCheck:!0,additionalDirectories:r}}var Fn=class{constructor(t={}){this.deps=t}name="codex";supportedPermissionModes=["default","plan","acceptEdits","bypassPermissions"];questionToolNames=[Mi];planApprovalToolNames=[];activityHeartbeatMs(){let t=Number.parseInt(process.env[Ac]??String(Pi),10);return!Number.isFinite(t)||t<=0?Pi:t}createCodexClient(){return this.deps.createCodex?.()??new xc}launch(t){let n=t.permissionMode==="plan",r=t.permissionMode==="plan"?"default":t.permissionMode,o=t.resumeSessionId,s=0,a=0,p=this.activityHeartbeatMs(),l,g,b=!1,q=!0,O,oe=[],Me=null,Qe=!1,$=!1;function Gn(){Me&&(Me(),Me=null)}function Fe(z){oe.push(z),Gn()}function Nr(){Qe=!0,Gn()}function Dn(z){!z||$||($=!0,Fe({type:"init",session_id:z}))}async function*Vi(){for(;;){for(;oe.length>0;)yield oe.shift();if(Qe)return;await new Promise(z=>{Me=z})}}let Xt=()=>Ec(t,r),Bi=()=>(l||(l=this.createCodexClient()),g||(q&&t.resumeSessionId?g=l.resumeThread(t.resumeSessionId,Xt()):o?g=l.resumeThread(o,Xt()):g=l.startThread(Xt())),b&&o&&(g=l.resumeThread(o,Xt()),b=!1),Dn(g.id??o??void 0),g),Kr=async z=>{let Bn=Date.now();a+=1;let Lr="",Hn=!1,Wn,Hi=q&&n?Cc(z):z,Jt=dt=>{Hn||(Hn=!0,Fe({type:"result",data:Fi(o??"",{duration_ms:Date.now()-Bn,total_cost_usd:s,num_turns:a,...dt,session_id:o??""})}))};try{let dt=Bi(),vr=dt.id??o??void 0;vr&&(o=vr,Dn(o)),O=new AbortController,t.abortController?.signal.aborted&&O.abort(t.abortController.signal.reason),Wn=setInterval(()=>{Fe({type:"activity"})},p);let Wi=await dt.runStreamed(Hi,{signal:O.signal});for await(let se of Wi.events){if(se.type==="thread.started"){o=se.thread_id,Dn(o);continue}if(se.type==="item.completed"){(se.item.type==="agent_message"||se.item.type==="reasoning")&&(Lr+=`${se.item.text}
|
|
43
|
+
`,Fe({type:"text",text:se.item.text}));continue}if(se.type==="error"){Fe({type:"text",text:`[codex:error] ${se.message}`});continue}if(se.type==="turn.failed"){Jt({success:!1,result:se.error.message,session_id:o??""});continue}if(se.type==="turn.completed"){s+=Oc(se.usage);let jr=Lr.slice(-500);Mn(jr)&&Fe({type:"tool_use",name:Mi,input:{text:jr}}),Jt({success:!0,session_id:o??""})}}Hn||Jt({success:!1,result:"Codex turn ended without terminal event",session_id:o??""})}catch(dt){Jt({success:!1,result:Ei(dt),session_id:o??""})}finally{Wn&&clearInterval(Wn),O=void 0,q=!1}},Vn=()=>{O?.abort(t.abortController?.signal.reason??"interrupted")};return t.abortController?.signal&&t.abortController.signal.addEventListener("abort",Vn),(async()=>{try{let z=t.prompt;if(typeof z=="string"){await Kr(z);return}for await(let Bn of z)if(t.abortController?.signal.aborted||(await Kr(Rc(Bn)),t.abortController?.signal.aborted))break}finally{t.abortController?.signal.removeEventListener("abort",Vn),Nr()}})().catch(z=>{Fe({type:"result",data:Fi(o??"",{success:!1,result:Ei(z),total_cost_usd:s,num_turns:a,session_id:o??""})}),t.abortController?.signal.removeEventListener("abort",Vn),Nr()}),{messages:Vi(),async setPermissionMode(z){r=z,b=!0},async interrupt(){O?.abort("interrupted")}}}buildUserMessage(t,n){return{type:"user",text:t,session_id:n}}};var kr=new Map;function ki(e){kr.set(e.name,e)}function $r(e){let t=kr.get(e);if(!t)throw new Error(`Unknown agent harness: "${e}". Available: ${[...kr.keys()].join(", ")}`);return t}function $i(){let e=y.defaultHarness??"claude-code";return $r(e)}ki(new Pn);ki(new Fn);var _i=200,$c=120*1e3;function En(e){return e instanceof Error?e.message:String(e)}var _c={starting:["running","failed","killed"],running:["completed","failed","killed"],completed:[],failed:[],killed:[]},_r=class{queue=[];resolve=null;done=!1;hasPending(){return this.queue.length>0}push(t){this.queue.push(t),this.resolve&&(this.resolve(),this.resolve=null)}end(){this.done=!0,this.resolve&&(this.resolve(),this.resolve=null)}async*[Symbol.asyncIterator](){for(;;){for(;this.queue.length>0;)yield this.queue.shift();if(this.done)return;await new Promise(t=>{this.resolve=t})}}},kn=class extends kc{id;name;harnessSessionId;harness;harnessHandle;prompt;workdir;model;reasoningEffort;systemPrompt;allowedTools;permissionMode;currentPermissionMode;pendingModeSwitch;resumeSessionId;forkSession;multiTurn;notifyOnTurnEnd;messageStream;_status="starting";error;startedAt;completedAt;abortController;outputBuffer=[];result;costUsd=0;originChannel;originThreadId;originAgentId;originSessionKey;pendingPlanApproval=!1;lobsterResumeToken;killReason="unknown";waitingForInputFired=!1;lastTurnHadQuestion=!1;planModeApproved=!1;autoRespondCount=0;timers=new Map;constructor(t,n){super(),this.id=Ci(8),this.name=n,this.harness=t.harness?$r(t.harness):$i();let r=this.harness.name==="codex";this.prompt=t.prompt,this.workdir=t.workdir,this.model=t.model??(r?y.model:void 0)??y.defaultModel,this.reasoningEffort=t.reasoningEffort??(r?y.reasoningEffort:void 0),this.systemPrompt=t.systemPrompt,this.allowedTools=t.allowedTools,this.permissionMode=t.permissionMode??y.permissionMode,this.currentPermissionMode=r&&this.permissionMode==="plan"?"default":this.permissionMode,this.originChannel=t.originChannel,this.originThreadId=t.originThreadId,this.originAgentId=t.originAgentId,this.originSessionKey=t.originSessionKey,this.resumeSessionId=t.resumeSessionId,this.forkSession=t.forkSession,this.multiTurn=t.multiTurn??!0,this.notifyOnTurnEnd=t.notifyOnTurnEnd??!0,this.startedAt=Date.now(),this.abortController=new AbortController}get status(){return this._status}get harnessName(){return this.harness.name}get duration(){return(this.completedAt??Date.now())-this.startedAt}get phase(){return this._status!=="running"?this._status:this.harness.name==="codex"?"implementing":this.pendingPlanApproval?"awaiting-plan-approval":this.currentPermissionMode==="plan"?"planning":"implementing"}transition(t){if(!_c[this._status].includes(t))throw new Error(`Session state error: cannot transition from ${this._status} to ${t}. This is an internal error \u2014 please report it.`);let n=this._status;this._status=t,this.emit("statusChange",this,t,n)}setTimer(t,n,r){this.clearTimer(t),this.timers.set(t,setTimeout(r,n))}clearTimer(t){let n=this.timers.get(t);n&&(clearTimeout(n),this.timers.delete(t))}clearAllTimers(){for(let t of this.timers.values())clearTimeout(t);this.timers.clear()}async start(){try{let t;this.multiTurn?(this.messageStream=new _r,this.messageStream.push(this.harness.buildUserMessage(this.prompt,"")),t=this.messageStream):t=this.prompt;let n=this.harness.launch({prompt:t,cwd:this.workdir,model:this.model,reasoningEffort:this.reasoningEffort,permissionMode:this.permissionMode,systemPrompt:this.systemPrompt,allowedTools:this.allowedTools,resumeSessionId:this.resumeSessionId,forkSession:this.forkSession,abortController:this.abortController,mcpServers:oi()});this.harnessHandle=n,this.setTimer("startup",$c,()=>{this._status==="starting"&&this.kill("startup-timeout")})}catch(t){let n=t instanceof Error?t.message:String(t);this.transitionToTerminal("failed",{error:n});return}this.consumeMessages(this.harnessHandle.messages).catch(t=>{let n=t instanceof Error?t.message:String(t),r=t instanceof Error?t.stack:void 0;console.error(`[Session ${this.id}] consumeMessages error: ${n}`,r),this.isActive&&this.transitionToTerminal("failed",{error:n})})}async sendMessage(t){if(this._status!=="running")throw new Error(`Session is not running (status: ${this._status})`);this.resetIdleTimer(),this.waitingForInputFired=!1;let n=t;if(this.pendingModeSwitch){let r=this.pendingModeSwitch,o=!1,s=!1;if(this.harnessHandle?.setPermissionMode)try{await this.harnessHandle.setPermissionMode(r),this.currentPermissionMode=r,this.pendingModeSwitch=void 0,s=!0,o=!0}catch(a){throw console.error(`[Session ${this.id}] setPermissionMode(${r}) FAILED: ${En(a)}`),this.pendingPlanApproval=!0,new Error(`Failed to switch permission mode to ${r}: ${En(a)}`)}else this.pendingModeSwitch=void 0,s=!0,o=!0,console.warn(`[Session ${this.id}] Cannot call setPermissionMode \u2014 falling back to text prefix only (currentPermissionMode remains ${this.currentPermissionMode})`);s&&(this.pendingPlanApproval=!1,r!=="plan"&&(this.planModeApproved=!0)),o&&(n=`[SYSTEM: The user has approved your plan. Exit plan mode immediately and implement the changes with full permissions. Do not ask for further confirmation.]
|
|
44
|
+
|
|
45
|
+
${t}`)}else if(this.pendingPlanApproval&&!this.planModeApproved){let r=this.harness.planApprovalToolNames;if(n=`[SYSTEM: The user wants changes to your plan. Revise the plan based on their feedback below,${r.length>0?` then call ${r.join(" or ")} again to re-submit for approval.`:" then re-submit your revised plan for approval."} Do NOT start implementing yet.]
|
|
46
|
+
|
|
47
|
+
${t}`,this.harnessHandle?.setPermissionMode)try{await this.harnessHandle.setPermissionMode("plan"),this.currentPermissionMode="plan"}catch(s){console.warn(`[Session ${this.id}] Failed to re-assert plan mode: ${En(s)}`)}}if(this.multiTurn&&this.messageStream)this.messageStream.push(this.harness.buildUserMessage(n,this.harnessSessionId??""));else if(this.harnessHandle?.streamInput){let r=this.harness.buildUserMessage(n,this.harnessSessionId??"");async function*o(){yield r}await this.harnessHandle.streamInput(o())}else throw new Error("Session does not support follow-up messages (launched in single-turn mode).")}async interrupt(){this.harnessHandle?.interrupt&&await this.harnessHandle.interrupt()}switchPermissionMode(t){this.pendingModeSwitch=t}get isActive(){return this._status==="starting"||this._status==="running"}kill(t){this.transitionToTerminal("killed",{reason:t})}complete(t="done"){this.transitionToTerminal("completed",{reason:t})}incrementAutoRespond(){this.autoRespondCount++}resetAutoRespond(){this.autoRespondCount=0}getOutput(t){return t===void 0?this.outputBuffer.slice():this.outputBuffer.slice(-t)}resetIdleTimer(){if(!this.multiTurn)return;let t=(y.idleTimeoutMinutes??15)*60*1e3;this.setTimer("idle",t,()=>{this._status==="running"&&this.kill("idle-timeout")})}teardown(){this.clearAllTimers(),this.completedAt||(this.completedAt=Date.now()),this.messageStream&&this.messageStream.end(),this.harnessHandle?.interrupt&&this.harnessHandle.interrupt().catch(t=>{console.warn(`[Session ${this.id}] interrupt during teardown failed: ${En(t)}`)}),this.abortController.abort()}transitionToTerminal(t,n={}){this.isActive&&(n.reason&&(this.killReason=n.reason),n.error!==void 0&&(this.error=n.error),this.completedAt=Date.now(),this.transition(t),this.teardown())}async consumeMessages(t){for await(let n of t){if(!this.isActive)break;if(this.resetIdleTimer(),n.type==="init")this.clearTimer("startup"),this.harnessSessionId=n.session_id,this._status==="starting"&&this.transition("running");else if(n.type==="text")this.waitingForInputFired=!1,this.pendingPlanApproval||(this.lastTurnHadQuestion=!1),this.outputBuffer.push(n.text),this.outputBuffer.length>_i&&this.outputBuffer.splice(0,this.outputBuffer.length-_i),this.emit("output",this,n.text);else if(n.type==="tool_use")this.harness.questionToolNames.includes(n.name)?(this.lastTurnHadQuestion=!0,this.currentPermissionMode==="plan"&&!this.planModeApproved&&(this.pendingPlanApproval=!0)):this.harness.planApprovalToolNames.includes(n.name)&&!this.planModeApproved&&(this.lastTurnHadQuestion=!0,this.pendingPlanApproval=!0),this.emit("toolUse",this,n.name,n.input);else if(n.type==="permission_mode_change"){let r=this.currentPermissionMode;this.currentPermissionMode=n.mode,n.mode!=="plan"&&r==="plan"&&!this.planModeApproved&&(this.pendingPlanApproval=!0,this.lastTurnHadQuestion=!0)}else if(n.type==="result"){if(this.result={subtype:n.data.success?"success":"error",duration_ms:n.data.duration_ms,total_cost_usd:n.data.total_cost_usd,num_turns:n.data.num_turns,result:n.data.result,is_error:!n.data.success,session_id:n.data.session_id},this.costUsd=n.data.total_cost_usd,this.multiTurn&&this.messageStream&&n.data.success){this.resetIdleTimer(),this.currentPermissionMode==="plan"&&!this.pendingPlanApproval&&!this.planModeApproved&&(this.pendingPlanApproval=!0);let o=this.pendingPlanApproval||this.lastTurnHadQuestion,s=this.messageStream?.hasPending()===!0;o&&!this.waitingForInputFired?(this.waitingForInputFired=!0,this.emit("turnEnd",this,!0)):s||o||(this.emit("turnEnd",this,!1),this.complete("done"))}else this.transitionToTerminal(n.data.success?"completed":"failed");this.lastTurnHadQuestion=!1}else n.type}}};import{mkdirSync as Uc,readFileSync as Nc,readdirSync as Kc,renameSync as Lc,statSync as vc,unlinkSync as jc,writeFileSync as Ui}from"fs";import{homedir as Gc,tmpdir as Ni}from"os";import{dirname as Dc,join as _n}from"path";function Vc(e){let t=e.OPENCLAW_HOME?.trim();return t||_n(Gc(),".openclaw")}function Bc(e){let t=e.OPENCLAW_CODE_AGENT_SESSIONS_PATH?.trim();return t||_n(Vc(e),"code-agent-sessions.json")}var Hc=new Set(["completed","failed","killed"]),Wc=new Set(["running","completed","failed","killed"]),qc=1440*60*1e3;function Ki(e){return e instanceof Error?e.message:String(e)}function zc(e){return!!e&&typeof e=="object"}function $n(e,t=""){return typeof e=="string"&&e.trim().length>0?e:t}function mt(e){return typeof e=="string"&&e.trim().length>0?e:void 0}function Li(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function Yc(e){return e==="low"||e==="medium"||e==="high"?e:void 0}function Xc(e){return e==="default"||e==="plan"||e==="acceptEdits"||e==="bypassPermissions"?e:void 0}function Jc(e){return e==="user"||e==="idle-timeout"||e==="startup-timeout"||e==="shutdown"||e==="done"||e==="unknown"?e:void 0}function Qc(e){if(typeof e=="string"&&Wc.has(e))return e==="running"?"killed":e}function Zc(e){if(!zc(e))return;let t=$n(e.harnessSessionId);if(!t)return;let n=Qc(e.status);if(n)return{sessionId:mt(e.sessionId),harnessSessionId:t,name:$n(e.name,t),prompt:$n(e.prompt),workdir:$n(e.workdir,"(unknown)"),model:mt(e.model),reasoningEffort:Yc(e.reasoningEffort),createdAt:Li(e.createdAt),completedAt:Li(e.completedAt),status:n,killReason:Jc(e.killReason),costUsd:typeof e.costUsd=="number"&&Number.isFinite(e.costUsd)?e.costUsd:0,originAgentId:mt(e.originAgentId),originChannel:mt(e.originChannel),originThreadId:typeof e.originThreadId=="string"||typeof e.originThreadId=="number"?e.originThreadId:void 0,originSessionKey:mt(e.originSessionKey),outputPath:mt(e.outputPath),harness:mt(e.harness),notifyOnTurnEnd:typeof e.notifyOnTurnEnd=="boolean"?e.notifyOnTurnEnd:void 0,currentPermissionMode:Xc(e.currentPermissionMode)}}var Un=class{persisted=new Map;idIndex=new Map;nameIndex=new Map;indexPath;constructor(t={}){let n=t.env??process.env;this.indexPath=t.indexPath??Bc(n),n.OPENCLAW_DEBUG_SESSION_STORE==="1"&&console.warn(`[SessionStore] index path: ${this.indexPath}`),this.loadIndex()}loadIndex(){try{let t=Nc(this.indexPath,"utf-8"),n=JSON.parse(t);if(!Array.isArray(n))return;let r=!1;for(let o of n){let s=Zc(o);if(!s){r=!0;continue}this.persisted.set(s.harnessSessionId,s),s.sessionId&&this.idIndex.set(s.sessionId,s.harnessSessionId),s.name&&this.nameIndex.set(s.name,s.harnessSessionId)}r&&this.saveIndex()}catch{}}saveIndex(){try{Uc(Dc(this.indexPath),{recursive:!0});let t=this.indexPath+".tmp";Ui(t,JSON.stringify([...this.persisted.values()],null,2),"utf-8"),Lc(t,this.indexPath)}catch(t){console.warn(`[SessionStore] Failed to save session index: ${Ki(t)}`)}}markRunning(t){if(!t.harnessSessionId)return;let n={sessionId:t.id,harnessSessionId:t.harnessSessionId,name:t.name,prompt:t.prompt,workdir:t.workdir,model:t.model,reasoningEffort:t.reasoningEffort,createdAt:t.startedAt,status:"running",costUsd:0,originAgentId:t.originAgentId,originChannel:t.originChannel,originThreadId:t.originThreadId,originSessionKey:t.originSessionKey,harness:t.harnessName,notifyOnTurnEnd:t.notifyOnTurnEnd,currentPermissionMode:t.currentPermissionMode};this.persisted.set(n.harnessSessionId,n),this.idIndex.set(t.id,n.harnessSessionId),this.nameIndex.set(t.name,n.harnessSessionId),this.saveIndex()}hasRecordedSession(t){return this.idIndex.has(t)}persistTerminal(t){if(!t.harnessSessionId)return;let n;try{let o=_n(Ni(),`openclaw-agent-${t.id}.txt`),s=t.getOutput().join(`
|
|
48
|
+
`);s.length>0&&(Ui(o,s,"utf-8"),n=o)}catch(o){console.warn(`[SessionStore] Failed to write output file for session ${t.id}: ${Ki(o)}`)}let r={sessionId:t.id,harnessSessionId:t.harnessSessionId,name:t.name,prompt:t.prompt,workdir:t.workdir,model:t.model,reasoningEffort:t.reasoningEffort,createdAt:t.startedAt,completedAt:t.completedAt,status:t.status,killReason:t.killReason,costUsd:t.costUsd,originAgentId:t.originAgentId,originChannel:t.originChannel,originThreadId:t.originThreadId,originSessionKey:t.originSessionKey,outputPath:n,harness:t.harnessName,notifyOnTurnEnd:t.notifyOnTurnEnd,currentPermissionMode:t.currentPermissionMode};this.persisted.set(t.harnessSessionId,r),this.idIndex.set(t.id,t.harnessSessionId),this.nameIndex.set(t.name,t.harnessSessionId),this.saveIndex()}getLatestPersistedByName(t){let n,r=Number.NEGATIVE_INFINITY,o=Number.NEGATIVE_INFINITY,s=Number.NEGATIVE_INFINITY,a=0;for(let p of this.persisted.values()){if(p.name!==t){a++;continue}let l=p.createdAt??Number.NEGATIVE_INFINITY,g=p.completedAt??Number.NEGATIVE_INFINITY;(l>r||l===r&&g>o||l===r&&g===o&&a>s)&&(n=p,r=l,o=g,s=a),a++}return n}resolveHarnessSessionId(t,n){if(n)return n;let r=this.idIndex.get(t);if(r&&this.persisted.has(r))return r;let o=this.getLatestPersistedByName(t);if(o)return o.harnessSessionId;if(this.persisted.has(t)||/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t))return t}getPersistedSession(t){let n=this.persisted.get(t);if(n)return n;let r=this.idIndex.get(t);return r?this.persisted.get(r):this.getLatestPersistedByName(t)}listPersistedSessions(){return[...this.persisted.values()].sort((t,n)=>(n.completedAt??0)-(t.completedAt??0))}cleanupTmpOutputFiles(t){try{let n=Ni(),r=Kc(n).filter(o=>o.startsWith("openclaw-agent-")&&o.endsWith(".txt"));for(let o of r)try{let s=_n(n,o),a=vc(s).mtimeMs;t-a>qc&&jc(s)}catch{}}catch{}}evictOldestPersisted(t){let n=this.listPersistedSessions();if(n.length<=t)return;let r=n.slice(t);for(let o of r){this.persisted.delete(o.harnessSessionId);for(let[s,a]of this.idIndex)a===o.harnessSessionId&&this.idIndex.delete(s);for(let[s,a]of this.nameIndex)a===o.harnessSessionId&&this.nameIndex.delete(s)}this.saveIndex()}shouldGcActiveSession(t,n,r){return!t.completedAt||!Hc.has(t.status)?!1:n-t.completedAt>r}};var el=new Set(["completed","failed","killed"]),Nn=class{metrics={totalCostUsd:0,costPerDay:new Map,sessionsByStatus:{completed:0,failed:0,killed:0},totalLaunched:0,totalDurationMs:0,sessionsWithDuration:0,mostExpensive:null};incrementLaunched(){this.metrics.totalLaunched++}recordSession(t){let n=t.costUsd??0,r=t.status;this.metrics.totalCostUsd+=n;let o=new Date(t.completedAt??t.startedAt).toISOString().slice(0,10);if(this.metrics.costPerDay.set(o,(this.metrics.costPerDay.get(o)??0)+n),el.has(r)&&this.metrics.sessionsByStatus[r]++,t.completedAt){let s=t.completedAt-t.startedAt;this.metrics.totalDurationMs+=s,this.metrics.sessionsWithDuration++}(!this.metrics.mostExpensive||n>this.metrics.mostExpensive.costUsd)&&(this.metrics.mostExpensive={id:t.id,name:t.name,costUsd:n,prompt:Je(t.prompt,80)})}getMetrics(){return{totalCostUsd:this.metrics.totalCostUsd,costPerDay:new Map(this.metrics.costPerDay),sessionsByStatus:{...this.metrics.sessionsByStatus},totalLaunched:this.metrics.totalLaunched,totalDurationMs:this.metrics.totalDurationMs,sessionsWithDuration:this.metrics.sessionsWithDuration,mostExpensive:this.metrics.mostExpensive?{...this.metrics.mostExpensive}:null}}};import{execFile as tl}from"child_process";import{randomUUID as nl}from"crypto";var vi=3e4,rl=2e3,ol=2e4,ji=4,Kn=class{notifications=null;pendingRetryTimers=new Set;setNotifications(t){this.notifications=t}clearPendingRetries(){for(let t of this.pendingRetryTimers)clearTimeout(t);this.pendingRetryTimers.clear()}deliverToTelegram(t,n){if(!this.notifications)return;let r=t.originChannel;!r||r==="unknown"||r==="gateway"||!r.includes("|")||this.notifications.emitToChannel(r,n,t.originThreadId)}buildDeliverArgs(t,n){if(!t||t==="unknown"||t==="gateway")return[];let r=t.split("|");if(r.length<2)return[];let o=[],a=(typeof n=="number"||typeof n=="string"&&n.trim().length>0)&&r[0]==="telegram"?`:topic:${n}`:"";return r.length>=3?o.push("--deliver","--reply-channel",r[0],"--reply-account",r[1],"--reply-to",r.slice(2).join("|")+a):o.push("--deliver","--reply-channel",r[0],"--reply-to",r[1]+a),o}retryDelayMs(t){let n=Math.max(0,t-1),r=rl*2**n;return Math.min(r,ol)}executeWithRetries(t,n,r=1){tl("openclaw",t,{timeout:vi},o=>{if(!o)return;if(r>=ji){console.error(`[WakeDispatcher] ${n.target} wake failed after ${r} attempts for ${n.label} session=${n.sessionId}: ${o.message}`),n.onFinalFailure?.();return}let s=this.retryDelayMs(r);console.error(`[WakeDispatcher] ${n.target} wake failed attempt ${r}/${ji} for ${n.label} session=${n.sessionId}: ${o.message}. Retrying in ${s}ms`);let a=setTimeout(()=>{this.pendingRetryTimers.delete(a),this.executeWithRetries(t,n,r+1)},s);this.pendingRetryTimers.add(a)})}fireChatSendWithRetry(t,n,r,o,s){let a=["gateway","call","chat.send","--expect-final","--timeout",String(vi),"--params",JSON.stringify({sessionKey:t,message:n,idempotencyKey:nl()})];this.executeWithRetries(a,{label:r,sessionId:o,target:"chat",onFinalFailure:s})}fireSystemEventWithRetry(t,n,r){let o=["system","event","--text",t,"--mode","now"];this.executeWithRetries(o,{label:n,sessionId:r,target:"system"})}wakeAgent(t,n,r,o){let s=t.originAgentId?.trim(),a=t.originSessionKey?.trim();if((!s||!a||o==="plan-approval")&&this.deliverToTelegram(t,r),!s||!a){this.fireSystemEventWithRetry(n,o,t.id);return}this.fireChatSendWithRetry(a,n,o,t.id,()=>{this.deliverToTelegram(t,r),this.fireSystemEventWithRetry(n,`${o}-fallback`,t.id)})}};function al(){let e=process.env.OPENCLAW_CODE_AGENT_PLAN_WORKFLOW_PATH?.trim();if(e)return e;let t=il(Di(import.meta.url)),n=[Ur(process.cwd(),"workflows","plan-approval.lobster"),Ur(t,"..","workflows","plan-approval.lobster"),Ur(t,"..","..","workflows","plan-approval.lobster")];for(let r of n)if(sl(r))return r;return Di(new URL("../workflows/plan-approval.lobster",import.meta.url))}var ul=al(),ml=new Set(["completed","failed","killed"]),Ln=new Set(["starting","running"]),dl=5e3,pl=3e4;function cl(e){let t=e.trim();if(!t)return;let n=s=>typeof s=="string"&&s.trim().length>0&&/^[A-Za-z0-9._:-]+$/.test(s.trim()),r=[t];for(let s of t.split(/\r?\n/)){let a=s.trim();a.startsWith("{")&&a.endsWith("}")&&r.push(a)}for(let s of r)try{let a=JSON.parse(s),p=a?.resumeToken??a?.requiresApproval?.resumeToken??a?.details?.requiresApproval?.resumeToken;if(n(p))return p.trim()}catch{}let o=t.match(/"resumeToken"\s*:\s*"([^"]+)"/);if(o&&n(o[1]))return o[1].trim()}var vn=class{sessions=new Map;maxSessions;maxPersistedSessions;_notifications=null;lastWaitingEventTimestamps=new Map;lastTurnCompleteMarkers=new Map;lastTerminalWakeMarkers=new Map;store;metrics;wakeDispatcher;constructor(t=5,n=50){this.maxSessions=t,this.maxPersistedSessions=n,this.store=new Un,this.metrics=new Nn,this.wakeDispatcher=new Kn}get persisted(){return this.store.persisted}get idIndex(){return this.store.idIndex}get nameIndex(){return this.store.nameIndex}set notifications(t){this._notifications=t,this.wakeDispatcher.setNotifications(t)}get notifications(){return this._notifications}uniqueName(t){let n=new Set([...this.sessions.values()].filter(o=>Ln.has(o.status)).map(o=>o.name));if(!n.has(t))return t;let r=2;for(;n.has(`${t}-${r}`);)r++;return`${t}-${r}`}spawn(t){if([...this.sessions.values()].filter(a=>Ln.has(a.status)).length>=this.maxSessions)throw new Error(`Max sessions reached (${this.maxSessions}). Use agent_sessions to list active sessions and agent_kill to end one.`);let r=t.name||mi(t.prompt),o=this.uniqueName(r);o!==r&&console.warn(`[SessionManager] Name conflict: "${r}" \u2192 "${o}" (active session with same name exists)`);let s=new kn(t,o);if(this.sessions.set(s.id,s),this.metrics.incrementLaunched(),this.notifications&&this.notifications.attachToSession(s),s.on("statusChange",(a,p)=>{p==="running"&&s.harnessSessionId?this.store.markRunning(s):ml.has(p)&&this.onSessionTerminal(s)}),s.on("turnEnd",(a,p)=>{this.onTurnEnd(s,p)}),s.start(),this.notifications){let a=`\u{1F680} [${s.name}] Launched | ${s.workdir} | ${s.model??"default"}`;this.deliverToTelegram(s,a)}return s}onSessionTerminal(t){if(this.persistSession(t),this.lastWaitingEventTimestamps.delete(t.id),t.killReason==="done")return;if(t.status==="completed"){if(!this.shouldEmitTerminalWake(t))return;this.triggerAgentEvent(t);return}if(t.status==="failed"){if(!this.shouldEmitTerminalWake(t))return;let p=t.error||t.result?.is_error&&t.result.result||t.result?.result||this.extractLastOutputLine(t)||`Session failed with no error details (session=${t.id}, subtype=${t.result?.subtype??"none"}, turns=${t.result?.num_turns??0})`,l=Je(p,200);this.triggerFailedEvent(t,l);return}let n=`$${(t.costUsd??0).toFixed(2)}`,r=_e(t.duration),s={user:"by agent/user","idle-timeout":`idle ${y.idleTimeoutMinutes??15}min`,shutdown:"gateway shutdown",unknown:""}[t.killReason]||"",a=`Killed${s?` (${s})`:""}`;this.deliverToTelegram(t,`\u26D4 [${t.name}] ${a} | ${n} | ${r}`)}persistSession(t){this.store.hasRecordedSession(t.id)||this.metrics.recordSession(t),this.store.persistTerminal(t)}getMetrics(){return this.metrics.getMetrics()}recordSessionMetrics(t){this.metrics.recordSession(t)}deliverToTelegram(t,n){this.wakeDispatcher.deliverToTelegram(t,n)}buildDeliverArgs(t,n){return this.wakeDispatcher.buildDeliverArgs(t,n)}runLobsterApproval(t,n){let r=JSON.stringify({session_id:t.id,session_name:t.name,plan_summary:n}),o=["--json","invoke","--tool","lobster","--args-json",JSON.stringify({action:"run",pipeline:ul,argsJson:r,timeoutMs:0})];Gi("openclaw",o,{timeout:pl},(s,a,p)=>{if(s){console.error(`[SessionManager] Lobster launch failed for session=${t.id}: ${s.message}`),this.deliverToTelegram(t,`\u{1F4CB} [${t.name}] Plan ready \u2014 Lobster gate failed, please review manually:
|
|
49
|
+
|
|
50
|
+
${Je(n,800)}`);return}let l=typeof a=="string"?a:String(a??""),g=typeof p=="string"?p:String(p??""),b=cl(`${l}
|
|
51
|
+
${g}`);if(!b){let O=`${l}
|
|
52
|
+
${g}`.trim().substring(0,200);console.warn(`[SessionManager] Lobster response missing resume token for session=${t.id}: ${O}`)}b&&(t.lobsterResumeToken=b);let q=[`\u{1F4CB} [${t.name}] Plan ready for approval`,"",Je(n,1200),"",`Session: ${t.name} (${t.id})`,"",'To approve: reply "approve"',"To reject: reply with feedback"];this.deliverToTelegram(t,q.join(`
|
|
53
|
+
`))})}resumeLobsterApproval(t,n){let r=n?3e4:1e4;return new Promise((o,s)=>{let a=["--json","invoke","--tool","lobster","--args-json",JSON.stringify({action:"resume",token:t,approve:n})];Gi("openclaw",a,{timeout:r},p=>{p?(console.error(`[SessionManager] Lobster resume failed (approve=${n}): ${p.message}`),s(p)):o()})})}debounceWaitingEvent(t){let n=Date.now(),r=this.lastWaitingEventTimestamps.get(t);return r&&n-r<dl?!1:(this.lastWaitingEventTimestamps.set(t,n),!0)}originThreadLine(t){return t.originThreadId!=null?`Session origin thread: ${t.originThreadId}`:""}extractLastOutputLine(t){return t.getOutput(3).filter(o=>o.trim()).pop()?.trim()||void 0}getOutputPreview(t,n=1e3){let r=t.getOutput(20).join(`
|
|
54
|
+
`);return r.length>n?pi(r,n):r}triggerAgentEvent(t){let n=this.getOutputPreview(t),r=["Coding agent session completed.",`Name: ${t.name} | ID: ${t.id}`,`Status: ${t.status}`,this.originThreadLine(t),"","Output preview:",n,"","[ACTION REQUIRED] Follow your autonomy rules for session completion:",`1. Use agent_output(session='${t.id}', full=true) to read the full result.`,"2. If this is part of a multi-phase pipeline, launch the next phase NOW \u2014 do not wait for user input.","3. Notify the user with a summary of what was done."].join(`
|
|
55
|
+
`),o=`$${(t.costUsd??0).toFixed(2)}`,s=_e(t.duration),a=`\u2705 [${t.name}] Completed | ${o} | ${s}`;this.wakeDispatcher.wakeAgent(t,r,a,"completed")}triggerFailedEvent(t,n){let r=this.getOutputPreview(t),o=r.trim()?["","Output preview:",r]:[],s=["Coding agent session failed.",`Name: ${t.name} | ID: ${t.id}`,`Status: ${t.status}`,this.originThreadLine(t),"","Failure summary:",n,...o,"","[ACTION REQUIRED] Follow your autonomy rules for session failure:",`1. Use agent_output(session='${t.id}', full=true) to inspect the full failure context.`,"2. If the failure is a launch/config issue or other recoverable error, relaunch the task now or continue it yourself.","3. Notify the user with the failure cause and the next action you are taking."].join(`
|
|
56
|
+
`),a=`$${(t.costUsd??0).toFixed(2)}`,p=_e(t.duration),l=[`\u274C [${t.name}] Failed | ${a} | ${p}`,` \u26A0\uFE0F ${n}`].join(`
|
|
57
|
+
`);this.wakeDispatcher.wakeAgent(t,s,l,"failed")}triggerWaitingForInputEvent(t){if(!this.debounceWaitingEvent(t.id))return;let n=this.getOutputPreview(t),r=t.pendingPlanApproval,o=r?`\u{1F4CB} [${t.name}] Plan ready for review:
|
|
58
|
+
|
|
59
|
+
${n}
|
|
60
|
+
|
|
61
|
+
Reply to approve or provide feedback.`:`\u{1F514} [${t.name}] Waiting for input`,s;if(r){let a=y.planApproval??"delegate";if(a==="ask"){this.runLobsterApproval(t,n);return}else a==="delegate"?s=["[DELEGATED PLAN APPROVAL] Coding agent session has finished its plan and is requesting approval to implement.",`Name: ${t.name} | ID: ${t.id}`,this.originThreadLine(t),"Permission mode: plan \u2192 will switch to bypassPermissions on approval","","\u26A0\uFE0F YOU MUST COMPLETE THESE STEPS IN ORDER. Do NOT skip any step.","","\u2501\u2501\u2501 STEP 1 (MANDATORY): Read the full plan \u2501\u2501\u2501",`Call agent_output(session='${t.id}', full=true) to read the FULL plan output.`,"The preview below is truncated \u2014 you MUST read the full output before making any decision.","","Preview (truncated):",n,"","\u2501\u2501\u2501 STEP 2 (MANDATORY): Notify the user \u2501\u2501\u2501","After reading the full plan, use the message tool to send the user a summary that includes:","- What files/components will be changed","- Risk level (low/medium/high) and why","- Scope: does this match the original task or has it expanded?","- Any concerns or assumptions the plan makes","This message creates accountability \u2014 you cannot approve blindly.","","\u2501\u2501\u2501 STEP 3 (ONLY AFTER steps 1 and 2): Decide \u2501\u2501\u2501","You are the delegated decision-maker. Choose ONE:","","APPROVE the plan directly if ALL of the following are true:","- You have read the FULL plan (not just the preview)","- You have sent the user the summary message","- The plan scope matches the original task request","- The changes are low-risk (no destructive operations, no credential handling, no production deployments)","- The plan is clear and well-scoped (no ambiguous requirements or open design questions)","- No architectural decisions that the user should weigh in on","- The working directory and codebase are correct","","ESCALATE to the user and WAIT if ANY of the following are true:","- The plan involves destructive operations (deleting files, dropping tables, force-pushing)","- The plan involves credentials, secrets, or production environments","- The plan requires architectural decisions not covered by the original task","- The scope has expanded beyond the original request","- The requirements are ambiguous or the plan makes assumptions the user should confirm","- You are unsure \u2014 when in doubt, always escalate","",`If approving: agent_respond(session='${t.id}', message='Approved. Go ahead.', approve=true)`,"If escalating: tell the user you need their decision and WAIT for his explicit response.",`To request changes: agent_respond(session='${t.id}', message='<your feedback>') \u2014 do NOT set approve=true. The agent will revise the plan.`].join(`
|
|
62
|
+
`):s=["[AUTO-APPROVE] Session has a plan ready. Approve it now:",`agent_respond(session='${t.id}', message='Approved. Go ahead.', approve=true)`].join(`
|
|
63
|
+
`)}else s=[`[SYSTEM INSTRUCTION: Follow your auto-respond rules strictly. If this is a permission request or "should I continue?" \u2192 auto-respond. For ALL other questions \u2192 forward the agent's EXACT question to the user. Do NOT add your own analysis, commentary, or interpretation. Do NOT "nudge" or "poke" the session.]`,"",`${t.multiTurn?"Multi-turn session":"Session"} is waiting for input.`,`Name: ${t.name} | ID: ${t.id}`,this.originThreadLine(t),"","Last output:",n,"",`Use agent_respond(session='${t.id}', message='...') to send a reply, or agent_output(session='${t.id}', full: true) to see full context before deciding.`].join(`
|
|
64
|
+
`);this.wakeDispatcher.wakeAgent(t,s,o,r?"plan-approval":"waiting")}onTurnEnd(t,n){if(t.notifyOnTurnEnd!==!1){if(n||t.pendingPlanApproval){this.triggerWaitingForInputEvent(t);return}this.shouldEmitTurnCompleteWake(t)&&this.triggerTurnCompleteEventWithSignal(t)}}shouldEmitTurnCompleteWake(t){let n=`${t.result?.session_id??""}|${t.result?.num_turns??0}|${t.result?.duration_ms??0}`;return this.lastTurnCompleteMarkers.get(t.id)===n?!1:(this.lastTurnCompleteMarkers.set(t.id,n),!0)}shouldEmitTerminalWake(t){let n=`${t.status}|${t.completedAt??0}|${t.result?.session_id??""}|${t.result?.num_turns??0}|${t.killReason}`;return this.lastTerminalWakeMarkers.get(t.id)===n?!1:(this.lastTerminalWakeMarkers.set(t.id,n),!0)}triggerTurnCompleteEventWithSignal(t){let n=this.getOutputPreview(t),r=Mn(n),o=`$${(t.costUsd??0).toFixed(2)}`,s=r?"yes":"no",a=`\u{1F504} [${t.name}] Turn done | ${o} | Waiting input: ${s}`,p=["Coding agent session turn ended.",`Name: ${t.name}`,`ID: ${t.id}`,`Status: ${t.status}`,"",`Looks like waiting for user input: ${s}`,"","Last output (~20 lines):",n,...this.originThreadLine(t)?["",this.originThreadLine(t)]:[]].join(`
|
|
65
|
+
`);this.deliverToTelegram(t,a),this.wakeDispatcher.wakeAgent(t,p,a,"turn-complete")}resolve(t){let n=this.sessions.get(t);if(n)return n;let r=[...this.sessions.values()].filter(s=>s.name===t);if(r.length===0)return;let o=r.filter(s=>Ln.has(s.status));return o.length>0?o.sort((s,a)=>a.startedAt-s.startedAt)[0]:r.sort((s,a)=>a.startedAt-s.startedAt)[0]}get(t){return this.sessions.get(t)}list(t){let n=[...this.sessions.values()];return t&&t!=="all"&&(n=n.filter(r=>r.status===t)),n.sort((r,o)=>o.startedAt-r.startedAt)}kill(t,n){let r=this.sessions.get(t);return r?(r.kill(n??"user"),!0):!1}killAll(t="user"){for(let n of this.sessions.values())Ln.has(n.status)&&this.kill(n.id,t);this.wakeDispatcher.clearPendingRetries()}resolveHarnessSessionId(t){let n=this.resolve(t);return this.store.resolveHarnessSessionId(t,n?.harnessSessionId)}getPersistedSession(t){return this.store.getPersistedSession(t)}listPersistedSessions(){return this.store.listPersistedSessions()}cleanup(){let t=Date.now(),n=(y.sessionGcAgeMinutes??1440)*6e4;for(let[r,o]of this.sessions)this.store.shouldGcActiveSession(o,t,n)&&(this.persistSession(o),this.sessions.delete(r),this.lastWaitingEventTimestamps.delete(r),this.lastTurnCompleteMarkers.delete(r),this.lastTerminalWakeMarkers.delete(r));this.store.cleanupTmpOutputFiles(t),this.store.evictOldestPersisted(this.maxPersistedSessions)}};var jn=class{sendMessage;constructor(t){this.sendMessage=t}attachToSession(t){}stop(){}emitToChannel(t,n,r){this.sendMessage(t,n,r)}};import{execFile as ll}from"child_process";function aR(e){let t=null,n=null,r=null;e.registerTool(o=>ui(o),{optional:!1}),e.registerTool(o=>ci(o),{optional:!1}),e.registerTool(o=>li(o),{optional:!1}),e.registerTool(o=>fi(o),{optional:!1}),e.registerTool(o=>Ii(o),{optional:!1}),e.registerTool(o=>hi(o),{optional:!1}),yi(e),xi(e),Si(e),Ti(e),bi(e),Ai(e),wi(e),e.registerService({id:"openclaw-code-agent",start:()=>{let o=e.pluginConfig??e.getConfig?.()??{};si(o),t=new vn(y.maxSessions,y.maxPersistedSessions),Fr(t);let s=(a,p,l)=>{let g="telegram",b="",q;if(y.fallbackChannel?.includes("|")){let $=y.fallbackChannel.split("|");$.length>=3&&$[0]&&$[1]?(g=$[0],q=$[1],b=$.slice(2).join("|")):$[0]&&$[1]&&(g=$[0],b=$[1])}let O=g,oe=b,Me=q;if(a==="unknown"||!a){if(!b){console.warn(`[code-agent] sendMessage: channelId="${a}" and no fallbackChannel configured`);return}}else if(a.includes("|")){let $=a.split("|");$.length>=3?(O=$[0],Me=$[1],oe=$.slice(2).join("|")):$[0]&&$[1]&&(O=$[0],oe=$[1])}else if(/^-?\d+$/.test(a))O="telegram",oe=a;else if(!b){console.warn(`[code-agent] sendMessage: unrecognized channelId="${a}" and no fallbackChannel configured`);return}let Qe=["message","send","--channel",O];Me&&Qe.push("--account",Me),Qe.push("--target",oe,"-m",p),l!=null&&Qe.push("--thread-id",String(l)),ll("openclaw",Qe,{timeout:15e3},($,Gn,Fe)=>{$&&(console.error(`[code-agent] sendMessage CLI ERROR: ${$.message}`),Fe&&console.error(`[code-agent] sendMessage CLI STDERR: ${Fe}`))})};n=new jn(s),Er(n),t.notifications=n,r=setInterval(()=>t.cleanup(),300*1e3)},stop:()=>{n&&n.stop(),t&&t.killAll("shutdown"),r&&clearInterval(r),r=null,t=null,n=null,Fr(null),Er(null)}})}export{aR as register};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "openclaw-code-agent",
|
|
3
|
+
"name": "OpenClaw Code Agent",
|
|
4
|
+
"description": "Orchestrate coding agent sessions from OpenClaw",
|
|
5
|
+
"version": "2.0.0",
|
|
6
|
+
"configSchema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"maxSessions": {
|
|
11
|
+
"type": "number",
|
|
12
|
+
"default": 5
|
|
13
|
+
},
|
|
14
|
+
"defaultModel": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Default model for new sessions (e.g. 'sonnet', 'opus')"
|
|
17
|
+
},
|
|
18
|
+
"model": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Override model for all Codex sessions (e.g. 'gpt-5.3-codex', 'gpt-5.4'). Falls back to agent model config if not set."
|
|
21
|
+
},
|
|
22
|
+
"reasoningEffort": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"enum": ["low", "medium", "high"],
|
|
25
|
+
"description": "Reasoning effort for Codex sessions. 'high' for complex tasks, 'medium' (default) for balance, 'low' for speed."
|
|
26
|
+
},
|
|
27
|
+
"defaultWorkdir": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Default working directory for new sessions"
|
|
30
|
+
},
|
|
31
|
+
"idleTimeoutMinutes": {
|
|
32
|
+
"type": "number",
|
|
33
|
+
"default": 15,
|
|
34
|
+
"description": "Idle timeout in minutes for multi-turn sessions before auto-kill"
|
|
35
|
+
},
|
|
36
|
+
"sessionGcAgeMinutes": {
|
|
37
|
+
"type": "number",
|
|
38
|
+
"default": 1440,
|
|
39
|
+
"description": "TTL in minutes before terminal runtime sessions are evicted from memory (persisted metadata remains resumable)."
|
|
40
|
+
},
|
|
41
|
+
"maxPersistedSessions": {
|
|
42
|
+
"type": "number",
|
|
43
|
+
"default": 50,
|
|
44
|
+
"description": "Maximum number of completed sessions to keep in memory for resume"
|
|
45
|
+
},
|
|
46
|
+
"fallbackChannel": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Fallback notification channel (e.g. 'telegram|123456789')"
|
|
49
|
+
},
|
|
50
|
+
"permissionMode": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"default": "plan",
|
|
53
|
+
"enum": [
|
|
54
|
+
"default",
|
|
55
|
+
"plan",
|
|
56
|
+
"acceptEdits",
|
|
57
|
+
"bypassPermissions"
|
|
58
|
+
],
|
|
59
|
+
"description": "Default permission mode for coding agent sessions"
|
|
60
|
+
},
|
|
61
|
+
"agentChannels": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"description": "Map of agent working directories to notification channels",
|
|
64
|
+
"additionalProperties": {
|
|
65
|
+
"type": "string"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"maxAutoResponds": {
|
|
69
|
+
"type": "number",
|
|
70
|
+
"default": 10,
|
|
71
|
+
"description": "Maximum consecutive auto-responds per session before requiring user input"
|
|
72
|
+
},
|
|
73
|
+
"planApproval": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"default": "delegate",
|
|
76
|
+
"enum": ["approve", "ask", "delegate"],
|
|
77
|
+
"description": "Plan approval behavior. 'delegate' (default): orchestrator autonomously decides whether to approve low-risk plans or escalate to the user. 'approve': orchestrator can auto-approve after verification. 'ask': orchestrator always forwards plans to the user for approval."
|
|
78
|
+
},
|
|
79
|
+
"defaultHarness": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"default": "claude-code",
|
|
82
|
+
"enum": ["claude-code", "codex"],
|
|
83
|
+
"description": "Default agent harness when agent_launch omits the harness parameter."
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"uiHints": {
|
|
88
|
+
"fallbackChannel": {
|
|
89
|
+
"label": "Fallback notification channel",
|
|
90
|
+
"help": "Default channel used when no workspace-specific agentChannels entry matches the session workdir.",
|
|
91
|
+
"placeholder": "telegram|my-bot|123456789",
|
|
92
|
+
"sensitive": true
|
|
93
|
+
},
|
|
94
|
+
"agentChannels": {
|
|
95
|
+
"label": "Workspace notification channels",
|
|
96
|
+
"help": "Map absolute working-directory paths to notification channels so each workspace routes session updates to the right chat.",
|
|
97
|
+
"sensitive": true
|
|
98
|
+
},
|
|
99
|
+
"defaultWorkdir": {
|
|
100
|
+
"label": "Default working directory",
|
|
101
|
+
"help": "Base directory used for new sessions when agent_launch does not provide a workdir.",
|
|
102
|
+
"placeholder": "/home/user/project"
|
|
103
|
+
},
|
|
104
|
+
"defaultModel": {
|
|
105
|
+
"label": "Default model",
|
|
106
|
+
"help": "Default model for new sessions when a harness-specific override is not set.",
|
|
107
|
+
"placeholder": "sonnet"
|
|
108
|
+
},
|
|
109
|
+
"model": {
|
|
110
|
+
"label": "Codex model override",
|
|
111
|
+
"help": "Override model for all Codex sessions; falls back to the agent model config or defaultModel when unset.",
|
|
112
|
+
"placeholder": "gpt-5.3-codex"
|
|
113
|
+
},
|
|
114
|
+
"maxSessions": {
|
|
115
|
+
"label": "Maximum concurrent sessions",
|
|
116
|
+
"help": "Upper limit on concurrently running coding-agent sessions."
|
|
117
|
+
},
|
|
118
|
+
"idleTimeoutMinutes": {
|
|
119
|
+
"label": "Idle timeout (minutes)",
|
|
120
|
+
"help": "Minutes a paused multi-turn session can remain idle before the plugin auto-kills it."
|
|
121
|
+
},
|
|
122
|
+
"permissionMode": {
|
|
123
|
+
"label": "Default permission mode",
|
|
124
|
+
"help": "Default approval mode used when new coding-agent sessions are launched."
|
|
125
|
+
},
|
|
126
|
+
"planApproval": {
|
|
127
|
+
"label": "Plan approval policy",
|
|
128
|
+
"help": "Choose whether plans are auto-approved, always escalated, or delegated to the orchestrator."
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"skills": ["./skills"]
|
|
132
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openclaw-code-agent",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "esbuild index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --minify --external:openclaw --external:openclaw/plugin-sdk --external:@anthropic-ai/claude-agent-sdk --external:@openai/codex-sdk",
|
|
8
|
+
"test": "tsx --test 'tests/**/*.test.ts'",
|
|
9
|
+
"typecheck": "tsc --noEmit"
|
|
10
|
+
},
|
|
11
|
+
"openclaw": {
|
|
12
|
+
"extensions": [
|
|
13
|
+
"./dist/index.js"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist/",
|
|
18
|
+
"skills/",
|
|
19
|
+
"workflows/",
|
|
20
|
+
"openclaw.plugin.json",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.37",
|
|
26
|
+
"@openai/codex-sdk": "^0.107.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@sinclair/typebox": "^0.34.48",
|
|
30
|
+
"@types/node": "^25.3.3",
|
|
31
|
+
"esbuild": "^0.27.3",
|
|
32
|
+
"nanoid": "^3.3.7",
|
|
33
|
+
"tsx": "^4.21.0",
|
|
34
|
+
"typescript": "^5.9.2"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"openclaw",
|
|
38
|
+
"plugin",
|
|
39
|
+
"claude",
|
|
40
|
+
"claude-code",
|
|
41
|
+
"coding-agent",
|
|
42
|
+
"codex",
|
|
43
|
+
"ai",
|
|
44
|
+
"anthropic",
|
|
45
|
+
"agent",
|
|
46
|
+
"code-generation",
|
|
47
|
+
"automation",
|
|
48
|
+
"cli",
|
|
49
|
+
"background-process",
|
|
50
|
+
"developer-tools"
|
|
51
|
+
],
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/goldmar/openclaw-code-agent.git"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/goldmar/openclaw-code-agent#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/goldmar/openclaw-code-agent/issues"
|
|
59
|
+
},
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"author": "Mark Goldenstein",
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"description": "An OpenClaw plugin that orchestrates coding agent sessions as managed background processes. Launch, monitor, and control coding agents (Claude Code, Codex, and more) directly from your AI gateway."
|
|
66
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Code Agent Orchestration
|
|
3
|
+
description: Skill for orchestrating coding agent sessions from OpenClaw. Covers launching, monitoring, multi-turn interaction, lifecycle management, notifications, and parallel work patterns.
|
|
4
|
+
metadata: {"openclaw": {"requires": {"plugins": ["openclaw-code-agent"]}}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Code Agent Orchestration
|
|
8
|
+
|
|
9
|
+
You orchestrate coding agent sessions via the `openclaw-code-agent`. Each session is an autonomous agent that executes code tasks in the background.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Launching sessions
|
|
14
|
+
|
|
15
|
+
### Mandatory rules
|
|
16
|
+
|
|
17
|
+
- **Notifications are routed automatically** via `agentChannels` config. Do NOT pass `channel` manually — it bypasses automatic routing.
|
|
18
|
+
- **Thread-aware routing**: When launched from a Telegram thread/topic, notifications are routed back to that same thread via `originThreadId`. This is handled automatically.
|
|
19
|
+
- **Always pass `multi_turn: true`** unless the task is a guaranteed one-shot with no possible follow-up.
|
|
20
|
+
- **Name the sessions** with `name` in kebab-case, short and descriptive.
|
|
21
|
+
- **Set `workdir`** to the target project directory, not the agent's workspace.
|
|
22
|
+
- **Default mode is `plan`**: Sessions start in plan mode. When the user approves a plan (e.g. "looks good", "go ahead"), the plugin automatically switches to `bypassPermissions` mode.
|
|
23
|
+
|
|
24
|
+
### Essential parameters
|
|
25
|
+
|
|
26
|
+
| Parameter | When to use |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `prompt` | Always. Clear and complete instruction. |
|
|
29
|
+
| `name` | Always. Descriptive kebab-case (`fix-auth-bug`, `add-dark-mode`). |
|
|
30
|
+
| `channel` | **Do NOT pass.** Resolved automatically via `agentChannels`. |
|
|
31
|
+
| `workdir` | Always when the project is not in the `defaultWorkdir`. |
|
|
32
|
+
| `multi_turn` | `true` by default unless explicitly one-shot. |
|
|
33
|
+
| `model` | When you want to force a specific model (`"sonnet"`, `"opus"`). |
|
|
34
|
+
| `system_prompt` | To inject project-specific context. |
|
|
35
|
+
| `permission_mode` | `"plan"` by default. `"bypassPermissions"` for trusted tasks. |
|
|
36
|
+
|
|
37
|
+
### Examples
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
# Simple task
|
|
41
|
+
agent_launch(
|
|
42
|
+
prompt: "Fix the null pointer in src/auth.ts line 42",
|
|
43
|
+
name: "fix-null-auth",
|
|
44
|
+
workdir: "/home/user/projects/myapp",
|
|
45
|
+
multi_turn: true
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Full feature
|
|
49
|
+
agent_launch(
|
|
50
|
+
prompt: "Implement dark mode toggle in the settings page. Use the existing theme context in src/context/theme.tsx. Add a toggle switch component and persist the preference in localStorage.",
|
|
51
|
+
name: "add-dark-mode",
|
|
52
|
+
workdir: "/home/user/projects/myapp",
|
|
53
|
+
multi_turn: true
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Resume and fork
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
# Resume a completed session
|
|
61
|
+
agent_launch(
|
|
62
|
+
prompt: "Continue. Also add error handling for the edge cases we discussed.",
|
|
63
|
+
resume_session_id: "fix-null-auth",
|
|
64
|
+
multi_turn: true
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Fork to try an alternative approach
|
|
68
|
+
agent_launch(
|
|
69
|
+
prompt: "Try a completely different approach: use middleware instead of decorators.",
|
|
70
|
+
resume_session_id: "refactor-db-repositories",
|
|
71
|
+
fork_session: true,
|
|
72
|
+
name: "refactor-db-middleware-approach",
|
|
73
|
+
multi_turn: true
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 2. Anti-cascade rules (CRITICAL)
|
|
80
|
+
|
|
81
|
+
**When woken by a waiting-for-input or completion event, you MUST ONLY use `agent_respond` or `agent_output` for the referenced session. NEVER launch new sessions in response to wake events.**
|
|
82
|
+
|
|
83
|
+
This prevents cascading session creation. The orchestrator exists to manage existing sessions, not to spawn new ones from wake events.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 3. Monitoring sessions
|
|
88
|
+
|
|
89
|
+
### List sessions
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
# All sessions
|
|
93
|
+
agent_sessions()
|
|
94
|
+
|
|
95
|
+
# Only running sessions
|
|
96
|
+
agent_sessions(status: "running")
|
|
97
|
+
|
|
98
|
+
# Completed sessions (for resume)
|
|
99
|
+
agent_sessions(status: "completed")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### View output
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
# Summary (last 50 lines)
|
|
106
|
+
agent_output(session: "fix-null-auth")
|
|
107
|
+
|
|
108
|
+
# Full output (up to 200 blocks)
|
|
109
|
+
agent_output(session: "fix-null-auth", full: true)
|
|
110
|
+
|
|
111
|
+
# Specific last N lines
|
|
112
|
+
agent_output(session: "fix-null-auth", lines: 100)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Interpreting session state
|
|
116
|
+
|
|
117
|
+
The `agent_output` header shows status, phase, cost, and duration:
|
|
118
|
+
```
|
|
119
|
+
Session: fix-auth [abc123] | Status: RUNNING | Phase: planning | Cost: $0.0312 | Duration: 2m15s
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The `Phase:` indicator for running sessions:
|
|
123
|
+
- `Phase: planning` — the agent is writing a plan
|
|
124
|
+
- `Phase: awaiting-plan-approval` — plan submitted, waiting for review
|
|
125
|
+
- `Phase: implementing` — actively writing code
|
|
126
|
+
|
|
127
|
+
The `agent_sessions` listing also shows phase and cost when available:
|
|
128
|
+
```
|
|
129
|
+
🟢 fix-auth [abc123] (2m15s | $0.03) — multi-turn
|
|
130
|
+
⚙️ Phase: planning
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Recency rule:** Always trust the Phase indicator and the *latest* (bottom) output lines. If earlier output mentions plan mode but Phase says `implementing`, the session has transitioned. Do NOT report it as "waiting for approval."
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 4. Multi-turn interaction
|
|
138
|
+
|
|
139
|
+
### Send a follow-up
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
# Reply to an agent question
|
|
143
|
+
agent_respond(session: "add-dark-mode", message: "Yes, use CSS variables for the theme colors.")
|
|
144
|
+
|
|
145
|
+
# Redirect a running session (interrupts the current turn)
|
|
146
|
+
agent_respond(session: "add-dark-mode", message: "Stop. Use Tailwind dark: classes instead of CSS variables.", interrupt: true)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Auto-respond rules (STRICT)
|
|
150
|
+
|
|
151
|
+
**Auto-respond immediately with `agent_respond`:**
|
|
152
|
+
- Permission requests to read/write files or run bash commands -> `"Yes, proceed."`
|
|
153
|
+
- Explicit confirmations like "Should I continue?" -> `"Yes, continue."`
|
|
154
|
+
|
|
155
|
+
**Forward to the user (everything else):**
|
|
156
|
+
- Architecture decisions (Redis vs PostgreSQL, REST vs GraphQL...)
|
|
157
|
+
- Destructive operations (deleting files, dropping tables...)
|
|
158
|
+
- Ambiguous requirements not covered by the initial prompt
|
|
159
|
+
- Scope changes ("This will require refactoring 15 files")
|
|
160
|
+
- Anything involving credentials, secrets, or production environments
|
|
161
|
+
- Questions about approach, design, or implementation choices
|
|
162
|
+
- Codebase clarification questions
|
|
163
|
+
- When in doubt -> always forward to the user
|
|
164
|
+
|
|
165
|
+
**When forwarding to the user, quote the agent's exact question. Do NOT add your own analysis, interpretation, or commentary.**
|
|
166
|
+
|
|
167
|
+
### Interaction cycle
|
|
168
|
+
|
|
169
|
+
1. Session launches -> runs in background
|
|
170
|
+
2. Wake event arrives when the session is waiting for input
|
|
171
|
+
3. Read the question with `agent_output(session, full: true)`
|
|
172
|
+
4. Decide: auto-respond (permissions/confirmations only) or forward
|
|
173
|
+
5. If auto-respond: `agent_respond(session, answer)`
|
|
174
|
+
6. If forward: relay the agent's exact question to the user, wait for their response, then `agent_respond`
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 5. Lifecycle management
|
|
179
|
+
|
|
180
|
+
### Stop or complete a session
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
# Kill a stuck/looping session
|
|
184
|
+
agent_kill(session: "fix-null-auth")
|
|
185
|
+
|
|
186
|
+
# Mark a session as successfully completed
|
|
187
|
+
agent_kill(session: "fix-null-auth", reason: "completed")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Use `agent_kill` (no reason) when:
|
|
191
|
+
- The session is stuck or looping
|
|
192
|
+
- The user requests a stop
|
|
193
|
+
|
|
194
|
+
Use `agent_kill(reason: "completed")` when:
|
|
195
|
+
- The turn output shows the task is done — this sends a `✅ Completed` notification
|
|
196
|
+
- Prefer this over letting the idle timer expire
|
|
197
|
+
|
|
198
|
+
### Idle completion and auto-resume
|
|
199
|
+
|
|
200
|
+
- After a turn completes without a question, the session is immediately **paused** (killed with reason `done`, auto-resumable).
|
|
201
|
+
- On the next `agent_respond` to a completed or idle-killed session, the plugin **auto-resumes** by spawning a new session with the same session ID — conversation context is preserved.
|
|
202
|
+
- Sessions idle for `idleTimeoutMinutes` (default: 15 min) are killed with reason `idle-timeout` and also auto-resume on next respond.
|
|
203
|
+
- Sessions killed explicitly by the user (`agent_kill` without `reason: "completed"`) do NOT auto-resume.
|
|
204
|
+
|
|
205
|
+
### Timeouts
|
|
206
|
+
|
|
207
|
+
- Idle multi-turn sessions are automatically killed after `idleTimeoutMinutes` (default: 15 min)
|
|
208
|
+
- Completed sessions are garbage-collected after 1h but remain resumable via persisted IDs
|
|
209
|
+
|
|
210
|
+
### Check the result after completion
|
|
211
|
+
|
|
212
|
+
When a session completes (completion wake event):
|
|
213
|
+
|
|
214
|
+
1. `agent_output(session: "xxx", full: true)` to read the result
|
|
215
|
+
2. Summarize briefly: files changed, cost, duration, any issues
|
|
216
|
+
3. If failed, analyze the error and decide: relaunch, fork, or escalate
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 6. Notifications
|
|
221
|
+
|
|
222
|
+
### Thread-based routing
|
|
223
|
+
|
|
224
|
+
Notifications are routed to the Telegram thread/topic where the session was launched. This is handled automatically via `originThreadId` — no manual configuration needed. The `agentChannels` config handles chat-level routing, and the thread ID handles within-chat routing.
|
|
225
|
+
|
|
226
|
+
### Events
|
|
227
|
+
|
|
228
|
+
| Event | What happens |
|
|
229
|
+
|---|---|
|
|
230
|
+
| Session starts | Silent (command response confirms launch) |
|
|
231
|
+
| Session completed | Brief one-liner to originating thread |
|
|
232
|
+
| Session failed | Error notification to originating thread |
|
|
233
|
+
| Waiting for input | Wake event + "Agent asks" in thread (only when the agent actually asks a question) |
|
|
234
|
+
| Session idle-killed | Brief notification with kill reason |
|
|
235
|
+
|
|
236
|
+
### Plan → Execute mode switch
|
|
237
|
+
|
|
238
|
+
Sessions start in `plan` mode by default. When you reply with **only** an approval keyword as the **entire message** (`"go ahead"`, `"implement"`, `"looks good"`, `"approved"`, `"lgtm"`, `"do it"`, `"proceed"`, `"execute"`, `"ship it"`), the plugin switches the session to `bypassPermissions` mode. The message must contain **only** the keyword — extra text will prevent the switch. To approve and also give instructions, send the approval keyword first, then send implementation details as a separate follow-up message.
|
|
239
|
+
|
|
240
|
+
### Plan approval modes
|
|
241
|
+
|
|
242
|
+
The `planApproval` config controls how the orchestrator handles plan-approval events:
|
|
243
|
+
|
|
244
|
+
- **`delegate`** (default): The orchestrator autonomously decides whether to approve or escalate each plan to the user. Approve when the plan is low-risk, well-scoped, and matches the original task. Escalate when the plan involves destructive operations, credentials/production, architectural decisions, scope expansion, or ambiguous requirements. When in doubt, always escalate.
|
|
245
|
+
- **`approve`**: The orchestrator can auto-approve straightforward, low-risk plans. Before approving, it verifies the working directory, codebase, and scope.
|
|
246
|
+
- **`ask`**: The orchestrator always forwards plans to the user. It never auto-approves on the user's behalf.
|
|
247
|
+
|
|
248
|
+
#### Delegate mode decision criteria
|
|
249
|
+
|
|
250
|
+
When operating in `delegate` mode, **approve** the plan directly if ALL of the following are true:
|
|
251
|
+
- The plan scope matches the original task request
|
|
252
|
+
- The changes are low-risk (no destructive operations, no credential handling, no production deployments)
|
|
253
|
+
- The plan is clear and well-scoped (no ambiguous requirements or open design questions)
|
|
254
|
+
- No architectural decisions that the user should weigh in on
|
|
255
|
+
- The working directory and codebase are correct
|
|
256
|
+
|
|
257
|
+
**Escalate** to the user (forward with 👋 and wait) if ANY of the following are true:
|
|
258
|
+
- Destructive operations (deleting files, dropping tables, force-pushing)
|
|
259
|
+
- Credentials, secrets, or production environments
|
|
260
|
+
- Architectural decisions not covered by the original task
|
|
261
|
+
- Scope expanded beyond the original request
|
|
262
|
+
- Ambiguous requirements or assumptions the user should confirm
|
|
263
|
+
- When in doubt — always escalate
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 7. Best practices
|
|
268
|
+
|
|
269
|
+
### Launch checklist
|
|
270
|
+
|
|
271
|
+
1. `agentChannels` is configured for this workdir -> notifications arrive
|
|
272
|
+
2. `multi_turn: true` -> interaction is possible after launch
|
|
273
|
+
3. `name` is descriptive -> easy to identify in `agent_sessions`
|
|
274
|
+
4. `workdir` points to the correct project -> the agent works in the right directory
|
|
275
|
+
|
|
276
|
+
### Parallel tasks
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
# Launch multiple sessions on independent tasks
|
|
280
|
+
agent_launch(prompt: "Build the frontend auth page", name: "frontend-auth", workdir: "/app/frontend", multi_turn: true)
|
|
281
|
+
agent_launch(prompt: "Build the backend auth API", name: "backend-auth", workdir: "/app/backend", multi_turn: true)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
- Respect the `maxSessions` limit (default: 5)
|
|
285
|
+
- Each session must have a unique `name`
|
|
286
|
+
- Monitor each session individually via wake events
|
|
287
|
+
|
|
288
|
+
### Reporting results
|
|
289
|
+
|
|
290
|
+
When a session completes, keep summaries brief:
|
|
291
|
+
- Files changed
|
|
292
|
+
- Cost and duration
|
|
293
|
+
- Any issues or remaining TODOs
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 8. Anti-patterns
|
|
298
|
+
|
|
299
|
+
| Anti-pattern | Consequence | Fix |
|
|
300
|
+
|---|---|---|
|
|
301
|
+
| Launching new sessions from wake events | Cascading sessions | Only use `agent_respond`/`agent_output` when woken |
|
|
302
|
+
| Adding commentary when forwarding questions | User gets noise, not the question | Quote the agent's exact question, nothing else |
|
|
303
|
+
| Auto-responding to design/architecture questions | Decisions made without user input | Only auto-respond to permissions and explicit confirmations |
|
|
304
|
+
| Passing `channel` explicitly | Bypasses automatic routing | Let `agentChannels` handle routing automatically |
|
|
305
|
+
| Not checking the result of a completed session | User doesn't know what happened | Always read `agent_output` and summarize briefly |
|
|
306
|
+
| Launching too many sessions in parallel | `maxSessions` limit reached | Respect the limit, prioritize, sequence if necessary |
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 9. Quick tool reference
|
|
311
|
+
|
|
312
|
+
| Tool | Usage | Key parameters |
|
|
313
|
+
|---|---|---|
|
|
314
|
+
| `agent_launch` | Launch a session | `prompt`, `name`, `workdir`, `multi_turn` |
|
|
315
|
+
| `agent_sessions` | List sessions | `status` (all/running/completed/failed/killed) |
|
|
316
|
+
| `agent_output` | Read the output | `session`, `full`, `lines` |
|
|
317
|
+
| `agent_kill` | Kill or complete a session | `session`, `reason` (`"completed"` or omit) |
|
|
318
|
+
| `agent_respond` | Send a follow-up | `session`, `message`, `interrupt` |
|
|
319
|
+
| `agent_stats` | Usage metrics | none |
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: plan-approval
|
|
2
|
+
args:
|
|
3
|
+
session_id:
|
|
4
|
+
required: true
|
|
5
|
+
session_name:
|
|
6
|
+
required: true
|
|
7
|
+
plan_summary:
|
|
8
|
+
required: true
|
|
9
|
+
steps:
|
|
10
|
+
- id: approve
|
|
11
|
+
approval: required
|
|
12
|
+
|
|
13
|
+
- id: proceed
|
|
14
|
+
command: openclaw.invoke --tool agent_respond --args-json '{"session":"$session_id","message":"Approved. Go ahead.","approve":true}'
|
|
15
|
+
condition: $approve.approved
|