@tloncorp/openclaw 0.4.3 → 0.6.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.
Files changed (104) hide show
  1. package/README.md +130 -141
  2. package/dist/index.js +147 -138
  3. package/dist/index.js.map +1 -1
  4. package/dist/setup-api.js +2 -2
  5. package/dist/setup-entry.js +2 -2
  6. package/dist/setup-entry.js.map +1 -1
  7. package/dist/src/account-fields.js +7 -3
  8. package/dist/src/account-fields.js.map +1 -1
  9. package/dist/src/actions.js +73 -52
  10. package/dist/src/actions.js.map +1 -1
  11. package/dist/src/channel.js +51 -39
  12. package/dist/src/channel.js.map +1 -1
  13. package/dist/src/channel.runtime.js +61 -32
  14. package/dist/src/channel.runtime.js.map +1 -1
  15. package/dist/src/config-schema.js +24 -4
  16. package/dist/src/config-schema.js.map +1 -1
  17. package/dist/src/effective-owner.js.map +1 -1
  18. package/dist/src/gateway-status.js +55 -7
  19. package/dist/src/gateway-status.js.map +1 -1
  20. package/dist/src/monitor/approval.js +71 -62
  21. package/dist/src/monitor/approval.js.map +1 -1
  22. package/dist/src/monitor/command-auth.js +7 -7
  23. package/dist/src/monitor/command-auth.js.map +1 -1
  24. package/dist/src/monitor/command-bridge.js +3 -2
  25. package/dist/src/monitor/command-bridge.js.map +1 -1
  26. package/dist/src/monitor/computing-presence.js +76 -12
  27. package/dist/src/monitor/computing-presence.js.map +1 -1
  28. package/dist/src/monitor/discovery.js +16 -9
  29. package/dist/src/monitor/discovery.js.map +1 -1
  30. package/dist/src/monitor/history.js +58 -26
  31. package/dist/src/monitor/history.js.map +1 -1
  32. package/dist/src/monitor/index.js +2771 -2483
  33. package/dist/src/monitor/index.js.map +1 -1
  34. package/dist/src/monitor/media.js +106 -78
  35. package/dist/src/monitor/media.js.map +1 -1
  36. package/dist/src/monitor/nudge-runner.js +36 -27
  37. package/dist/src/monitor/nudge-runner.js.map +1 -1
  38. package/dist/src/monitor/nudge-state.js +7 -11
  39. package/dist/src/monitor/nudge-state.js.map +1 -1
  40. package/dist/src/monitor/owner-reply-persistence.js +27 -26
  41. package/dist/src/monitor/owner-reply-persistence.js.map +1 -1
  42. package/dist/src/monitor/processed-messages.js.map +1 -1
  43. package/dist/src/monitor/settings-sync.js +1 -8
  44. package/dist/src/monitor/settings-sync.js.map +1 -1
  45. package/dist/src/monitor/utils.js +77 -71
  46. package/dist/src/monitor/utils.js.map +1 -1
  47. package/dist/src/nudge-decision.js +40 -43
  48. package/dist/src/nudge-decision.js.map +1 -1
  49. package/dist/src/nudge-messages.js +9 -9
  50. package/dist/src/nudge-scheduler.js.map +1 -1
  51. package/dist/src/owner-listen-command.js +38 -28
  52. package/dist/src/owner-listen-command.js.map +1 -1
  53. package/dist/src/pending-nudge.js.map +1 -1
  54. package/dist/src/runtime.js +10 -6
  55. package/dist/src/runtime.js.map +1 -1
  56. package/dist/src/session-roles.js +2 -1
  57. package/dist/src/session-roles.js.map +1 -1
  58. package/dist/src/settings.js +233 -102
  59. package/dist/src/settings.js.map +1 -1
  60. package/dist/src/setup-core.js +32 -32
  61. package/dist/src/setup-core.js.map +1 -1
  62. package/dist/src/setup-surface.js +19 -19
  63. package/dist/src/setup-surface.js.map +1 -1
  64. package/dist/src/shared-state.js +46 -0
  65. package/dist/src/shared-state.js.map +1 -0
  66. package/dist/src/targets.js +17 -10
  67. package/dist/src/targets.js.map +1 -1
  68. package/dist/src/telemetry.js +17 -14
  69. package/dist/src/telemetry.js.map +1 -1
  70. package/dist/src/tlon-binary.js +20 -12
  71. package/dist/src/tlon-binary.js.map +1 -1
  72. package/dist/src/tlon-tool-guard.js +5 -5
  73. package/dist/src/tool-trace.js +17 -13
  74. package/dist/src/tool-trace.js.map +1 -1
  75. package/dist/src/types.js +30 -12
  76. package/dist/src/types.js.map +1 -1
  77. package/dist/src/urbit/api-client.js +16 -12
  78. package/dist/src/urbit/api-client.js.map +1 -1
  79. package/dist/src/urbit/auth.js +9 -9
  80. package/dist/src/urbit/auth.js.map +1 -1
  81. package/dist/src/urbit/base-url.js +11 -11
  82. package/dist/src/urbit/base-url.js.map +1 -1
  83. package/dist/src/urbit/channel-ops.js +25 -19
  84. package/dist/src/urbit/channel-ops.js.map +1 -1
  85. package/dist/src/urbit/context.js +8 -8
  86. package/dist/src/urbit/context.js.map +1 -1
  87. package/dist/src/urbit/errors.js +33 -7
  88. package/dist/src/urbit/errors.js.map +1 -1
  89. package/dist/src/urbit/fetch.js +3 -3
  90. package/dist/src/urbit/fetch.js.map +1 -1
  91. package/dist/src/urbit/http-poke.js +10 -10
  92. package/dist/src/urbit/http-poke.js.map +1 -1
  93. package/dist/src/urbit/send.js +27 -23
  94. package/dist/src/urbit/send.js.map +1 -1
  95. package/dist/src/urbit/sse-client.js +45 -41
  96. package/dist/src/urbit/sse-client.js.map +1 -1
  97. package/dist/src/urbit/story.js +31 -30
  98. package/dist/src/urbit/story.js.map +1 -1
  99. package/dist/src/urbit/upload.js +8 -8
  100. package/dist/src/urbit/upload.js.map +1 -1
  101. package/dist/src/version.generated.js +2 -1
  102. package/dist/src/version.generated.js.map +1 -1
  103. package/openclaw.plugin.json +37 -0
  104. package/package.json +35 -15
package/README.md CHANGED
@@ -4,17 +4,17 @@ Tlon/Urbit channel plugin for [OpenClaw](https://github.com/openclaw/openclaw).
4
4
 
5
5
  ## Features
6
6
 
7
- - **DMs**: Receive and respond to direct messages
8
- - **Group Channels**: Participate in group chats (mention-triggered or open mode)
9
- - **Thread Replies**: Support for threaded conversations
10
- - **Rich Content**: Images, links, and formatted text
11
- - **Ship Authorization**: Allowlist ships for DM access
12
- - **Channel Authorization**: Per-channel ship allowlists with open/restricted modes
13
- - **Approval System**: Approve/deny new DMs, channel mentions, and group invites via DM
14
- - **Settings Store**: Hot-reload config via Urbit settings-store (no restart needed)
15
- - **Auto-Discovery**: Automatically monitors all channels in joined groups
16
- - **Cite Resolution**: Parse and fetch quoted message content
17
- - **Optional Telemetry**: Explicit PostHog opt-in for hosted analytics
7
+ - **DMs**: Receive and respond to direct messages
8
+ - **Group Channels**: Participate in group chats (mention-triggered or open mode)
9
+ - **Thread Replies**: Support for threaded conversations
10
+ - **Rich Content**: Images, links, and formatted text
11
+ - **Ship Authorization**: Allowlist ships for DM access
12
+ - **Channel Authorization**: Per-channel ship allowlists with open/restricted modes
13
+ - **Approval System**: Approve/deny new DMs, channel mentions, and group invites via DM
14
+ - **Settings Store**: Hot-reload config via Urbit settings-store (no restart needed)
15
+ - **Auto-Discovery**: Automatically monitors all channels in joined groups
16
+ - **Cite Resolution**: Parse and fetch quoted message content
17
+ - **Optional Telemetry**: Explicit PostHog opt-in for hosted analytics
18
18
 
19
19
  ## Installation
20
20
 
@@ -22,82 +22,81 @@ This plugin is included with OpenClaw. Enable it in your config:
22
22
 
23
23
  ```yaml
24
24
  channels:
25
- tlon:
26
- enabled: true
27
- ship: "~your-ship"
28
- url: "https://your-ship.tlon.network"
29
- code: "your-access-code"
25
+ tlon:
26
+ enabled: true
27
+ ship: '~your-ship'
28
+ url: 'https://your-ship.tlon.network'
29
+ code: 'your-access-code'
30
30
  ```
31
31
 
32
32
  ### Full Configuration Example
33
33
 
34
34
  ```yaml
35
35
  channels:
36
- tlon:
37
- enabled: true
38
- ship: "~your-ship"
39
- url: "https://your-ship.tlon.network"
40
- code: "your-access-code"
41
-
42
- # Owner receives approval requests and can manage the bot
43
- ownerShip: "~your-main-ship"
44
-
45
- # Ships allowed to DM the bot directly
46
- dmAllowlist:
47
- - "~trusted-friend"
48
- - "~another-ship"
49
-
50
- # Auto-accept settings
51
- autoAcceptDmInvites: true # Accept DMs from ships in dmAllowlist
52
- autoAcceptGroupInvites: false # Require approval for group invites
53
-
54
- # Channel discovery
55
- autoDiscoverChannels: true # Monitor all channels in joined groups
56
- groupChannels: # Additional channels to monitor explicitly
57
- - "chat/~host-ship/channel-name"
58
-
59
- # Per-channel authorization
60
- authorization:
61
- channelRules:
62
- "chat/~host/public-channel":
63
- mode: "open" # Anyone can interact
64
- "chat/~host/private-channel":
65
- mode: "restricted"
66
- allowedShips:
67
- - "~specific-ship"
68
-
69
- # Ships authorized by default for restricted channels
70
- defaultAuthorizedShips:
71
- - "~always-allowed"
72
-
73
- # Show model info in responses
74
- showModelSignature: false
75
-
76
- # Optional PostHog telemetry. Disabled unless explicitly enabled.
77
- telemetry:
78
- enabled: true
79
- apiKey: "phc_your_project_api_key"
80
- host: "https://us.i.posthog.com"
36
+ tlon:
37
+ enabled: true
38
+ ship: '~your-ship'
39
+ url: 'https://your-ship.tlon.network'
40
+ code: 'your-access-code'
41
+
42
+ # Owner receives approval requests and can manage the bot
43
+ ownerShip: '~your-main-ship'
44
+
45
+ # Ships allowed to DM the bot directly
46
+ dmAllowlist:
47
+ - '~trusted-friend'
48
+ - '~another-ship'
49
+
50
+ # Auto-accept settings
51
+ autoAcceptDmInvites: true # Accept DMs from ships in dmAllowlist
52
+ autoAcceptGroupInvites: false # Require approval for group invites
53
+
54
+ # Channel discovery
55
+ autoDiscoverChannels: true # Monitor all channels in joined groups
56
+ groupChannels: # Additional channels to monitor explicitly
57
+ - 'chat/~host-ship/channel-name'
58
+
59
+ # Per-channel authorization
60
+ authorization:
61
+ channelRules:
62
+ 'chat/~host/public-channel':
63
+ mode: 'open' # Anyone can interact
64
+ 'chat/~host/private-channel':
65
+ mode: 'restricted'
66
+ allowedShips:
67
+ - '~specific-ship'
68
+
69
+ # Ships authorized by default for restricted channels
70
+ defaultAuthorizedShips:
71
+ - '~always-allowed'
72
+
73
+ # Show model info in responses
74
+ showModelSignature: false
75
+
76
+ # Optional PostHog telemetry. Disabled unless explicitly enabled.
77
+ telemetry:
78
+ enabled: true
79
+ apiKey: 'phc_your_project_api_key'
80
+ host: 'https://us.i.posthog.com'
81
81
  ```
82
82
 
83
83
  ## Telemetry
84
84
 
85
- Telemetry is disabled by default. The plugin only sends tlemetry events when `channels.tlon.telemetry.enabled`
86
- is set to `true` and an API key is configured.
85
+ Telemetry is disabled by default. The plugin only sends telemetry events when `channels.tlon.telemetry.enabled` is set to `true` and an API key is configured.
87
86
 
88
- When enabled, the plugin captures a single `TlonBot Reply Handled` event each time it enters the OpenClaw reply
89
- flow. The event summarizes OpenClaw usage (tools used, character count, etc.), but does not log message content.
87
+ Every event carries content-free version identity: `harness: "openclaw"`, `pluginVersion`, `pluginCommit`, `pluginFingerprint`, plus Hermes-compatible `adapterVersion` and `adapterFingerprint` fields for shared PostHog charts.
90
88
 
91
- The plugin does not enable telemetry automatically just because an API key is present. `enabled: true` is
92
- required so open-source installs do not phone home by default.
89
+ When enabled, the plugin captures `TlonBot Gateway Connected` after subscriptions are active, `TlonBot Reply Handled` after each OpenClaw reply flow, `TlonBot Outbound Routed` for route-dependent sends, and heartbeat nudge events. `TlonBot Gateway Connected` also includes the resolved `tlon` CLI version as `tlonSkillVersion`. These summarize counts, routing, model/tool usage, and delivery status, but do not log message content.
90
+
91
+ The plugin does not enable telemetry automatically just because an API key is present. `enabled: true` is required so open-source installs do not phone home by default.
93
92
 
94
93
  ## Approval System
95
94
 
96
95
  The approval system lets you control who can interact with your bot. When `ownerShip` is configured, you'll receive DM notifications for:
97
96
 
98
- - **DM requests** from ships not on your `dmAllowlist`
99
- - **Channel mentions** from ships not authorized for that channel
100
- - **Group invites** (if `autoAcceptGroupInvites` is false)
97
+ - **DM requests** from ships not on your `dmAllowlist`
98
+ - **Channel mentions** from ships not authorized for that channel
99
+ - **Group invites** (if `autoAcceptGroupInvites` is false)
101
100
 
102
101
  ### Usage
103
102
 
@@ -110,29 +109,41 @@ New DM request from ~sampel-palnet:
110
109
  Reply "approve", "deny", or "block" (ID: dm-1234567890-abc)
111
110
  ```
112
111
 
113
- - **approve**: Allow the interaction and add to allowlist. Original message is processed.
114
- - **deny**: Reject silently. Ship can try again later.
115
- - **block**: Permanently block using Tlon's native blocking.
112
+ - **approve**: Allow the interaction and add to allowlist. Original message is processed.
113
+ - **deny**: Reject silently. Ship can try again later.
114
+ - **block**: Permanently block using Tlon's native blocking.
116
115
 
117
116
  ### Admin Commands
118
117
 
119
118
  The owner can send these commands via DM:
120
119
 
121
- | Command | Description |
122
- | --------------- | ------------------------------ |
123
- | `blocked` | List all blocked ships |
124
- | `pending` | List pending approval requests |
125
- | `unblock ~ship` | Unblock a ship |
120
+ | Command | Description |
121
+ | -------------- | ------------------------------------------ |
122
+ | `tlon version` | Show OpenClaw Tlon plugin version identity |
123
+ | `tlon-version` | Legacy alias for `tlon version` |
124
+ | `banned` | List all blocked ships |
125
+ | `pending` | List pending approval requests |
126
+ | `unban ~ship` | Unblock a ship |
127
+
128
+ `/tlon version` and the legacy `/tlon-version` alias report the same field-per-line summary as Hermes:
129
+
130
+ ```
131
+ Harness: OpenClaw
132
+ Adapter Version: 0.4.3
133
+ Tlon Skill: 0.3.2
134
+ Fingerprint: fp1:8aa23ca2bc8d
135
+ Source: no git checkout
136
+ ```
126
137
 
127
138
  ## Bundled Skill
128
139
 
129
140
  This plugin bundles [@tloncorp/tlon-skill](https://www.npmjs.com/package/@tloncorp/tlon-skill) which provides CLI commands for:
130
141
 
131
- - Contacts and profile management
132
- - Channel listing and history
133
- - Group administration
134
- - Message posting and reactions
135
- - Settings management
142
+ - Contacts and profile management
143
+ - Channel listing and history
144
+ - Group administration
145
+ - Message posting and reactions
146
+ - Settings management
136
147
 
137
148
  The skill is automatically available to your agent. For standalone usage, see the [tlon-skill repo](https://github.com/tloncorp/tlon-skill).
138
149
 
@@ -144,19 +155,19 @@ Full documentation: https://docs.openclaw.ai/channels/tlon
144
155
 
145
156
  ### Prerequisites
146
157
 
147
- - Docker
148
- - [GitHub CLI](https://cli.github.com/) (`gh`) authenticated with tloncorp access
149
- - A local Urbit ship (e.g., on `http://localhost:8080`)
150
- - Anthropic API key (or OpenRouter for alternative models)
158
+ - Docker
159
+ - [GitHub CLI](https://cli.github.com/) (`gh`) authenticated with tloncorp access
160
+ - A local Urbit ship (e.g., on `http://localhost:8080`)
161
+ - Anthropic API key (or OpenRouter for alternative models)
151
162
 
152
163
  ### Quick Start
153
164
 
154
165
  ```bash
155
- # 1. Clone this repo
156
- gh repo clone tloncorp/openclaw-tlon
157
- cd openclaw-tlon
166
+ # 1. Clone the tlon-apps monorepo (this package lives at packages/openclaw)
167
+ gh repo clone tloncorp/tlon-apps
168
+ cd tlon-apps/packages/openclaw
158
169
 
159
- # 2. Run setup (clones tlonbot + tlon-apps, creates .env)
170
+ # 2. Run setup (clones tlonbot next to the monorepo, creates .env)
160
171
  ./dev/setup.sh
161
172
 
162
173
  # 3. Edit .env with your credentials
@@ -176,48 +187,26 @@ docker compose --env-file .env -f dev/docker-compose.yml up --build
176
187
  ```
177
188
  parent/
178
189
  ├── tlonbot/ # Bot prompts + image-search extension (optional)
179
- ├── tlon-apps/ # Source repo for dev-only local @tloncorp/api overrides
180
- │ # (or another checkout such as "homestead")
181
- └── openclaw-tlon/ # This repo
190
+ └── tlon-apps/ # This monorepo
191
+ └── packages/
192
+ ├── api/ # @tloncorp/api (workspace dependency)
193
+ ├── tlon-skill/ # @tloncorp/tlon-skill (workspace dependency)
194
+ └── openclaw/ # This package
182
195
  ```
183
196
 
184
- `@tloncorp/api` and `@tloncorp/tlon-skill` are installed via npm for normal installs. During Docker dev, the entrypoint will also link a local `@tloncorp/api` override from `${TLON_APPS_DIR:-../tlon-apps}/packages/api` when that checkout has been built.
185
-
186
- The dev override uses the real `tlon-apps/packages/api` package, similar to the old `api-beta` workflow. You still rebuild `tlon-apps` separately so its `dist/` stays current, and the container startup will link that local package instead of using the published npm copy.
187
-
188
- If your local checkout is named `homestead` instead of `tlon-apps`, set:
197
+ `@tloncorp/api` and `@tloncorp/tlon-skill` are `workspace:^` dependencies, so on the host your editor and TypeScript resolve them straight from the sibling packages no linking needed. After changing the API surface, rebuild it so `dist/` stays current:
189
198
 
190
199
  ```bash
191
- export TLON_APPS_DIR=/absolute/path/to/homestead
200
+ pnpm --filter @tloncorp/api build
192
201
  ```
193
202
 
194
- `pnpm dev`, `pnpm dev:api:link`, and the Docker dev override will all use that path.
195
-
196
- If you want to modify `@tloncorp/api` locally while working in this repo, first link the local package into `openclaw-tlon` so your editor and local TypeScript resolve against `${TLON_APPS_DIR:-../tlon-apps}/packages/api`:
197
-
198
- ```bash
199
- pnpm dev:api:link
200
- ```
201
-
202
- That makes `openclaw-tlon` resolve `@tloncorp/api` to `${TLON_APPS_DIR:-../tlon-apps}/packages/api` on your machine. After changing the API surface, rebuild it there:
203
-
204
- ```bash
205
- pnpm --dir "${TLON_APPS_DIR:-../tlon-apps}" --filter @tloncorp/api build
206
- ```
207
-
208
- To switch back to the published npm package on your host:
209
-
210
- ```bash
211
- pnpm dev:api:unlink
212
- ```
203
+ Inside the Docker dev container, the package is copied out of the workspace and its workspace deps are resolved to published npm versions; the entrypoint then links local overrides for `@tloncorp/api` (from the mounted monorepo's `packages/api`, when built) and `@tloncorp/tlon-skill` (built from source) on top.
213
204
 
214
205
  ### Making Changes
215
206
 
216
- 1. Edit code in this repo or `${TLON_APPS_DIR:-../tlon-apps}/packages/api`
217
- 2. If you changed the API package, rebuild it first:
218
- `pnpm --dir "${TLON_APPS_DIR:-../tlon-apps}" --filter @tloncorp/api build`
207
+ 1. Edit code in this package or `packages/api`
208
+ 2. If you changed the API package, rebuild it first: `pnpm --filter @tloncorp/api build`
219
209
  3. Restart container: `docker compose --env-file .env -f dev/docker-compose.yml up --build`
220
- 4. For faster iteration, run OpenClaw directly on host with npm link
221
210
 
222
211
  ## Testing
223
212
 
@@ -257,11 +246,11 @@ pnpm test:integration
257
246
 
258
247
  This will:
259
248
 
260
- - Start 3 fakezod ships (~zod as bot, ~ten as test user, ~mug as third party)
261
- - Build and start an OpenClaw container with the plugin
262
- - Wait for ships, gateway, and SSE subscriptions
263
- - Run all test cases in `test/cases/`
264
- - Tear everything down on exit
249
+ - Start 3 fakezod ships (~zod as bot, ~ten as test user, ~mug as third party)
250
+ - Build and start an OpenClaw container with the plugin
251
+ - Wait for ships, gateway, and SSE subscriptions
252
+ - Run all test cases in `test/cases/`
253
+ - Tear everything down on exit
265
254
 
266
255
  **Run a specific test:**
267
256
 
@@ -279,7 +268,7 @@ For iterative development — you manage the ships and gateway yourself, tests r
279
268
  ./dev/setup.sh
280
269
  ```
281
270
 
282
- This clones `tlonbot` and `tlon-apps` as sibling directories and creates `.env` from `.env.example` if it doesn't exist. `@tloncorp/api` still installs from npm by default, but the dev container will link a local override from `tlon-apps/packages/api` so you can test API changes without publishing first.
271
+ This clones `tlonbot` next to the tlon-apps monorepo and creates `.env` from `.env.example` if it doesn't exist. Inside the dev container, the entrypoint links a local `@tloncorp/api` override from the monorepo's `packages/api` (when built) so you can test API changes without publishing first.
283
272
 
284
273
  **2. Edit `.env`:**
285
274
 
@@ -340,16 +329,16 @@ pnpm test:manual
340
329
 
341
330
  This will:
342
331
 
343
- - Start 3 fakezod ships and the OpenClaw gateway in Docker
344
- - Wait for everything to be ready
345
- - Print ship URLs, access codes, and gateway address
346
- - Tail gateway logs (Ctrl+C to stop and tear down)
332
+ - Start 3 fakezod ships and the OpenClaw gateway in Docker
333
+ - Wait for everything to be ready
334
+ - Print ship URLs, access codes, and gateway address
335
+ - Tail gateway logs (Ctrl+C to stop and tear down)
347
336
 
348
337
  Ships are accessible via browser:
349
338
 
350
- - **~zod** (bot): http://localhost:8080 — code: `lidlut-tabwed-pillex-ridrup`
351
- - **~ten** (user): http://localhost:8081 — code: `lapseg-nolmel-riswen-hopryc`
352
- - **~mug** (3rd party): http://localhost:8082 — code: `ravsut-bolryd-hapsum-pastul`
339
+ - **~zod** (bot): http://localhost:8080 — code: `lidlut-tabwed-pillex-ridrup`
340
+ - **~ten** (user): http://localhost:8081 — code: `lapseg-nolmel-riswen-hopryc`
341
+ - **~mug** (3rd party): http://localhost:8082 — code: `ravsut-bolryd-hapsum-pastul`
353
342
 
354
343
  To tear down without attaching to logs:
355
344
 
@@ -369,12 +358,12 @@ To test the `image_search` tool and Brave-powered `web_search`:
369
358
 
370
359
  1. Get a [Brave Search API key](https://brave.com/search/api/)
371
360
  2. Add to `.env`: `BRAVE_API_KEY=BSA...`
372
- 3. Clone [tlonbot](https://github.com/tloncorp/tlonbot) as a sibling directory:
373
- ```bash
374
- cd .. && gh repo clone tloncorp/tlonbot
375
- ```
361
+ 3. Clone [tlonbot](https://github.com/tloncorp/tlonbot) next to the tlon-apps monorepo root:
362
+ ```bash
363
+ gh repo clone tloncorp/tlonbot ../../../tlonbot
364
+ ```
376
365
 
377
- When `../tlonbot` exists, `docker-compose.local.yml` mounts it automatically, making the `image-search` plugin available. The entrypoint patches the config to load it if present.
366
+ When that checkout exists (override the location with `TLONBOT_DIR`), `docker-compose.local.yml` mounts it automatically, making the `image-search` plugin available. The entrypoint patches the config to load it if present.
378
367
 
379
368
  Without these, `web_search` falls back to whatever provider is available, and `image_search` returns a "no API key" error — tests that don't depend on image search still pass.
380
369