@strayl/agent 0.1.0 → 0.1.1

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 (59) hide show
  1. package/dist/agent.js +18 -1
  2. package/package.json +1 -1
  3. package/src/agent.ts +25 -0
  4. package/src/stdin-listener.ts +1 -2
  5. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_10_1772719084959.json +0 -1
  6. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_1_1772715451195.json +0 -1
  7. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_2_1772715452775.json +0 -1
  8. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_3_1772715454851.json +0 -1
  9. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_4_1772715457128.json +0 -1
  10. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_5_1772715464496.json +0 -1
  11. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_6_1772715466914.json +0 -1
  12. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_7_1772717269500.json +0 -1
  13. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_8_1772717274176.json +0 -1
  14. package/.strayl/checkpoints/03bdaa7e-3d71-44ee-9ff1-e23947c145b4/cp_9_1772717277769.json +0 -1
  15. package/.strayl/checkpoints/3f487378-fdba-413d-8c85-71e219cf4029/cp_1_1772710881634.json +0 -1
  16. package/.strayl/checkpoints/3f487378-fdba-413d-8c85-71e219cf4029/cp_2_1772710883272.json +0 -1
  17. package/.strayl/checkpoints/4c4d6f62-9e95-4790-9642-dced72212054/cp_1_1772722301047.json +0 -1
  18. package/.strayl/checkpoints/60ad3c6e-7e08-4841-9244-db9ef1351f94/cp_1_1772723527023.json +0 -1
  19. package/.strayl/checkpoints/6144a383-958f-478e-9f08-b3e435671beb/cp_1_1772723741593.json +0 -1
  20. package/.strayl/checkpoints/64d547ea-9114-4eac-8066-8c1d1cfafce3/cp_1_1772722436077.json +0 -1
  21. package/.strayl/checkpoints/88adc272-3c4f-410d-971a-dccd3a5a7c55/cp_1_1772723725211.json +0 -1
  22. package/.strayl/checkpoints/995ba1b6-6a4c-41c5-8a24-59d8475e31d4/cp_1_1772715363842.json +0 -1
  23. package/.strayl/checkpoints/aa9e0d03-bebe-49fb-b20d-7b5e5a3f299c/cp_1_1772715381414.json +0 -1
  24. package/.strayl/checkpoints/b0c6c2a4-a34d-451a-8937-c4235b306b2e/cp_1_1772711029179.json +0 -1
  25. package/.strayl/checkpoints/b4123afa-4e61-4a6e-b629-ef82273fb9af/cp_1_1772721833257.json +0 -1
  26. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_10_1772715577535.tmp +0 -0
  27. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_1_1772715557995.json +0 -1
  28. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_2_1772715560232.json +0 -1
  29. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_3_1772715562207.json +0 -1
  30. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_4_1772715564077.json +0 -1
  31. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_5_1772715566552.json +0 -1
  32. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_6_1772715569518.json +0 -1
  33. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_7_1772715571538.json +0 -1
  34. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_8_1772715573416.json +0 -1
  35. package/.strayl/checkpoints/b7fcfea4-6d41-4b31-98bd-e442f5b48f98/cp_9_1772715575737.json +0 -1
  36. package/.strayl/checkpoints/d42651a8-3bb2-4456-8775-66088ed31aca/cp_2_1772711043652.json +0 -1
  37. package/.strayl/checkpoints/ea81c9e2-47f7-4446-865d-40cb369d7944/cp_1_1772722131331.json +0 -1
  38. package/.strayl/checkpoints/eaaa83aa-18e4-4e74-be3a-1b1113f58dbe/cp_1_1772715398883.json +0 -1
  39. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_10_1772721968386.json +0 -1
  40. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_1_1772721929655.json +0 -1
  41. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_2_1772721935883.json +0 -1
  42. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_3_1772721940170.json +0 -1
  43. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_4_1772721945158.json +0 -1
  44. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_5_1772721949901.json +0 -1
  45. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_6_1772721954449.json +0 -1
  46. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_7_1772721957297.json +0 -1
  47. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_8_1772721961731.json +0 -1
  48. package/.strayl/checkpoints/ebb33f2b-ec62-4dba-9d84-7ccc4a54a255/cp_9_1772721964921.json +0 -1
  49. package/.strayl/checkpoints/f94498fa-cf5b-48de-a1f9-4c4754695dce/cp_1_1772715412172.json +0 -1
  50. package/.strayl/checkpoints/f94498fa-cf5b-48de-a1f9-4c4754695dce/cp_2_1772715414557.json +0 -1
  51. package/.strayl/checkpoints/f94498fa-cf5b-48de-a1f9-4c4754695dce/cp_3_1772715416369.json +0 -1
  52. package/.strayl/checkpoints/f94498fa-cf5b-48de-a1f9-4c4754695dce/cp_4_1772715421585.json +0 -1
  53. package/.strayl/logs/bg_1772680728576.log +0 -2
  54. package/INTEGRATION-PLAN.md +0 -385
  55. package/build.ts +0 -14
  56. package/hello.txt +0 -4
  57. package/run.sh +0 -84
  58. package/test-hitl.sh +0 -90
  59. package/tsconfig.json +0 -15
@@ -1,385 +0,0 @@
1
- # Интеграция strayl-agent SDK в strayl-app
2
-
3
- ## Текущая архитектура (что заменяем)
4
-
5
- ```
6
- Frontend (React)
7
- → SSE POST /api/chat/stream (Next.js serverless, 800s maxDuration)
8
- → createCodeAgent() — LangGraph agent работает IN-PROCESS
9
- → Daytona SDK вызывает sandbox удалённо (exec, read, write через API)
10
- → SSE events стримятся обратно
11
- → EventBuffer сохраняет events в DB для reconnect
12
- ```
13
-
14
- **Проблемы**: Agent умирает при timeout Vercel. LangGraph/deepagents — 200+ deps, постоянные патчи. Agent вне sandbox — все API ключи в env серверлесс функции.
15
-
16
- ## Новая архитектура
17
-
18
- ```
19
- Frontend (React)
20
- → SSE POST /api/chat/stream (Next.js serverless, thin proxy)
21
- → Daytona PTY: запускает `node agent.js --prompt "..." --model pro` ВНУТРИ sandbox
22
- → PTY onData → парсит JSON-per-line → SSE events обратно фронтенду
23
- → EventBuffer сохраняет events в DB (reconnect без изменений)
24
- ```
25
-
26
- **Ключевое изменение**: Serverless функция больше НЕ запускает LangGraph. Она только:
27
- 1. Создаёт/находит sandbox (как сейчас)
28
- 2. Запускает `node agent.js` через PTY (как exec tool)
29
- 3. Парсит stdout → SSE events
30
- 4. Обрабатывает HITL (пишет файлы в sandbox)
31
- 5. Пишет events в EventBuffer для reconnect
32
-
33
- **Agent SDK** уже установлен в sandbox snapshot (`strayl-sandbox`). Никакой npm install не нужен.
34
-
35
- ---
36
-
37
- ## Фаза 1: Sandbox Snapshot
38
-
39
- ### 1.1 Обновить strayl-sandbox Docker image
40
-
41
- Добавить в snapshot:
42
- ```dockerfile
43
- # Pre-install strayl-agent SDK
44
- COPY strayl-agent/dist/agent.js /opt/strayl/agent.js
45
- ```
46
-
47
- Один файл 470KB, zero deps. При обновлении SDK — пересобрать snapshot.
48
-
49
- ### 1.2 Env vars для sandbox
50
-
51
- При создании sandbox добавить:
52
- ```typescript
53
- envVars: {
54
- // Существующие
55
- CHAT_ID: chatId,
56
- PROJECT_SLUG: projectSlug,
57
- BRANCH_NAME: branchName,
58
- ...devEnvVars,
59
- // Новые для SDK
60
- STRAYL_SESSION_TOKEN: sessionToken, // Auth для proxy
61
- STRAYL_API_URL: "https://api.strayl.dev",
62
- STRAYL_USERNAME: username,
63
- STRAYL_PROJECT_SLUG: projectSlug,
64
- }
65
- ```
66
-
67
- Не передаём: OPENROUTER_API_KEY, GEMINI_API_KEY, TAVILY_API_KEY — они живут только в api.strayl.dev.
68
-
69
- ---
70
-
71
- ## Фаза 2: Agent Runner (замена LangGraph)
72
-
73
- ### 2.1 Новый файл: `strayl-app/lib/agent-runner.ts`
74
-
75
- ```typescript
76
- /**
77
- * Запускает strayl-agent SDK внутри sandbox через Daytona PTY.
78
- * Парсит JSON-per-line stdout → вызывает callback для каждого event.
79
- */
80
- export async function runAgentInSandbox(config: {
81
- sandboxId: string;
82
- prompt: string;
83
- model: string;
84
- mode: "normal" | "plan" | "implement";
85
- sessionId: string; // agent session (for checkpoints)
86
- sessionToken: string; // auth token for proxy
87
- username: string;
88
- projectSlug: string;
89
- blockedTools?: string[];
90
- maxIterations?: number;
91
- images?: string[]; // R2 URLs → download in sandbox before start
92
- previousSummary?: string;
93
- restoreCheckpoint?: string; // checkpoint file path
94
- onEvent: (event: AgentEvent) => void;
95
- onHitlRequest: (request: HitlRequest) => Promise<HitlResponse>;
96
- }): Promise<{ exitCode: number }> {
97
-
98
- // Build CLI args
99
- const args = [
100
- "--model", config.model,
101
- "--mode", config.mode,
102
- "--prompt", config.prompt,
103
- "--session-id", config.sessionId,
104
- "--work-dir", "workspace",
105
- "--hitl-dir", "/tmp/hitl",
106
- ];
107
- if (config.blockedTools?.length) {
108
- args.push("--blocked-tools", config.blockedTools.join(","));
109
- }
110
- if (config.maxIterations) {
111
- args.push("--max-iterations", String(config.maxIterations));
112
- }
113
- if (config.previousSummary) {
114
- args.push("--previous-summary", config.previousSummary);
115
- }
116
- if (config.restoreCheckpoint) {
117
- args.push("--restore-checkpoint", config.restoreCheckpoint);
118
- }
119
-
120
- const command = `node /opt/strayl/agent.js ${args.map(a => shellEscape(a)).join(" ")}`;
121
-
122
- const daytona = getDaytona();
123
- const sandbox = await daytona.get(config.sandboxId);
124
-
125
- let lineBuffer = ""; // Buffer for incomplete JSON lines
126
-
127
- // PTY — bidirectional: stdout for events, stdin for commands
128
- const ptyHandle = await sandbox.process.createPty({
129
- id: `agent-${config.sessionId}`,
130
- cols: 200,
131
- rows: 50,
132
- onData: (data: Uint8Array) => {
133
- const text = new TextDecoder().decode(data);
134
- lineBuffer += text;
135
-
136
- // Parse complete lines
137
- const lines = lineBuffer.split("\n");
138
- lineBuffer = lines.pop() || ""; // Keep incomplete last line in buffer
139
-
140
- for (const line of lines) {
141
- const trimmed = line.trim();
142
- if (!trimmed) continue;
143
- try {
144
- const event = JSON.parse(trimmed);
145
- config.onEvent(event);
146
- } catch {
147
- // Not JSON — raw PTY noise, ignore
148
- }
149
- }
150
- },
151
- });
152
-
153
- await ptyHandle.waitForConnection();
154
-
155
- // Start the agent
156
- await ptyHandle.sendInput(
157
- new TextEncoder().encode(`cd workspace && ${command}\nexit $?\n`)
158
- );
159
-
160
- // Return send function so caller can communicate with agent
161
- const sendToAgent = (cmd: object) => {
162
- ptyHandle.sendInput(
163
- new TextEncoder().encode(JSON.stringify(cmd) + "\n")
164
- );
165
- };
166
-
167
- return { ptyHandle, sendToAgent };
168
- }
169
- ```
170
-
171
- **Communication with running agent via PTY stdin:**
172
-
173
- ```typescript
174
- // HITL response (user answered a question)
175
- sendToAgent({
176
- type: "hitl-response",
177
- id: "original-tool-call-id",
178
- decision: "edit",
179
- data: { answer: "blue" }
180
- });
181
-
182
- // User sends new message while agent is working
183
- sendToAgent({
184
- type: "inject",
185
- text: "actually change the color to red"
186
- });
187
-
188
- // User confirms plan
189
- sendToAgent({ type: "confirm-plan" });
190
-
191
- // User cancels
192
- sendToAgent({ type: "cancel" });
193
-
194
- // User rolls back to checkpoint
195
- sendToAgent({ type: "rollback", iteration: 5 });
196
- ```
197
- ```
198
-
199
- ### 2.2 Маппинг events
200
-
201
- SDK events → существующие SSE events фронтенда (минимальные изменения в UI):
202
-
203
- | SDK event | SSE event (текущий) | Изменения в UI |
204
- |-----------|-------------------|----------------|
205
- | `session-start` | `run-started` | Добавить маппинг |
206
- | `text-delta` | `text-delta` | Без изменений |
207
- | `reasoning-delta` | `reasoning-delta` | Без изменений |
208
- | `tool-call-start` | `tool-call` | Переименовать |
209
- | `tool-result` | `tool-result` | Без изменений |
210
- | `exec-log` | `exec_log` | snake_case → as-is |
211
- | `file-complete` | `file-content-delta` | Адаптировать |
212
- | `hitl-request` | `interrupted` | Маппинг args → HITL UI |
213
- | `activity` | **НОВЫЙ** | Индикатор в UI |
214
- | `todos-update` | **НОВЫЙ** | Todo panel |
215
- | `session-end` | `run-finished` | Маппинг exit_reason |
216
- | `usage-update` | `credits-update` | Конвертация tokens → credits |
217
- | `plan-confirmed` | `plan-confirmed` | Без изменений |
218
- | `mode-changed` | **НОВЫЙ** | Mode indicator |
219
- | `checkpoint-saved` | — | Не стримить, только для DB |
220
- | `summarizing`/`summarized` | `summarization-complete` | Маппинг |
221
-
222
- ---
223
-
224
- ## Фаза 3: Замена route.ts
225
-
226
- ### 3.1 Упрощение `/api/chat/stream/route.ts`
227
-
228
- Текущий файл: **1200+ строк**. Новый: **~300 строк**.
229
-
230
- Что остаётся:
231
- - Auth check
232
- - Create/find sandbox
233
- - Git setup (clone, branch) — без изменений
234
- - Credit gate
235
- - EventBuffer → DB
236
- - SSE stream setup + heartbeat
237
-
238
- Что удаляется:
239
- - `createCodeAgent()` и весь LangGraph
240
- - `extractStreamingFileContent()` — SDK делает это сам
241
- - Все tool-specific обработчики
242
- - `beforeModel`/`wrapToolCall` middleware
243
- - Checkpointer (SDK сохраняет свои checkpoints в sandbox)
244
-
245
- Что добавляется:
246
- - `runAgentInSandbox()` вызов
247
- - Event маппинг SDK → SSE
248
- - HITL bridge (frontend → file in sandbox)
249
-
250
- ### 3.2 HITL Flow (через PTY stdin, не файлы)
251
-
252
- ```
253
- 1. Agent вызывает askUser → SDK emits hitl-request → PTY stdout → JSON line
254
- 2. route.ts парсит → SSE "interrupted" с type=ask-user, options=[...]
255
- 3. Frontend показывает UI → юзер выбирает
256
- 4. Frontend POST /api/chat/stream с resume={decisions: [...]}
257
- 5. route.ts → sendToAgent({ type: "hitl-response", id, decision, data })
258
- → PTY stdin → agent's StdinListener парсит → agent продолжает
259
- ```
260
-
261
- **Не нужен** файловый HITL transport (exec call) — PTY stdin быстрее и проще.
262
- Файловый HITL остаётся как fallback в SDK, но proxy его не использует.
263
-
264
- ### 3.3 Plan Mode Flow
265
-
266
- ```
267
- 1. Frontend отправляет message с planMode=true
268
- 2. route.ts запускает: node agent.js --mode plan --prompt "..."
269
- 3. Agent исследует, вызывает writePlan → hitl-request → PTY stdout
270
- 4. route.ts → SSE "interrupted" с type=plan, content=...
271
- 5. Frontend показывает план → юзер подтверждает
272
- 6. route.ts → sendToAgent({ type: "confirm-plan" }) → PTY stdin
273
- 7. SDK делает context reset → продолжает в implement mode
274
- ```
275
-
276
- ### 3.4 Inject (юзер пишет во время работы агента)
277
-
278
- ```
279
- 1. Юзер пишет "actually make it red" пока агент работает
280
- 2. Frontend POST /api/chat/stream с inject=true
281
- 3. route.ts → sendToAgent({ type: "inject", text: "actually make it red" })
282
- 4. SDK добавляет в контекст → agent увидит на следующей итерации
283
- ```
284
-
285
- ---
286
-
287
- ## Фаза 4: Reconnect (без изменений!)
288
-
289
- EventBuffer остаётся как есть:
290
- - route.ts буферит events и POST'ит в DB через API
291
- - `/api/chat/events/[runId]` — polling endpoint без изменений
292
- - Frontend `use-agent-stream.ts` — reconnect через sessionStorage без изменений
293
- - `_seq` из SDK events используется напрямую
294
-
295
- Единственное изменение: маппинг event types в EventBuffer.
296
-
297
- ---
298
-
299
- ## Фаза 5: Frontend Changes
300
-
301
- ### 5.1 `use-agent-stream.ts`
302
-
303
- Минимальные изменения:
304
- - Добавить обработку `activity` events → новый стейт `currentActivity`
305
- - Добавить обработку `mode-changed` events
306
- - Маппинг SDK event names → существующие UI event names (adapter слой)
307
- - HITL resume: без изменений (тот же POST /api/chat/stream с resume)
308
-
309
- ### 5.2 Новый компонент: `ActivityIndicator`
310
-
311
- ```tsx
312
- function ActivityIndicator({ activity }: { activity: ActivityType }) {
313
- const labels: Record<ActivityType, string> = {
314
- "creating-todos": "Creating to-do list...",
315
- "asking-user": "Formulating question...",
316
- "planning": "Writing plan...",
317
- "reading-file": "Reading file...",
318
- "writing-file": "Writing file...",
319
- "running-command": "Running command...",
320
- // ...
321
- };
322
- return <div className="activity-pill">{labels[activity]}</div>;
323
- }
324
- ```
325
-
326
- ### 5.3 Удалить из фронтенда
327
-
328
- - Все `file-content-delta` parsing (SDK стримит готовые events)
329
- - `extractStreamingFileContent` клиентский код
330
- - Plan mode toggle logic → упрощается (SDK управляет mode)
331
-
332
- ---
333
-
334
- ## Фаза 6: Удаление LangGraph
335
-
336
- После полной интеграции:
337
-
338
- ### Удалить файлы:
339
- - `strayl-app/langgraph/` — весь каталог (~30 файлов)
340
- - `strayl-app/langgraph/backends/daytona.ts` — DaytonaSandboxBackend
341
- - `strayl-app/langgraph/tools/` — все инструменты (теперь в SDK)
342
- - `strayl-app/langgraph/agent.ts` — createCodeAgent (1500+ строк)
343
- - `strayl-app/patches/` — все patch-package файлы
344
-
345
- ### Удалить зависимости из package.json:
346
- ```
347
- @langchain/core
348
- @langchain/langgraph
349
- @langchain/openai
350
- deepagents
351
- patch-package
352
- ```
353
-
354
- ### Ожидаемый результат:
355
- - `node_modules/` уменьшается на 200+ пакетов
356
- - route.ts: 1200 строк → ~300 строк
357
- - Нет patch-package, нет хаков
358
- - Agent переживает timeout Vercel (живёт в sandbox)
359
- - API ключи не в sandbox env
360
-
361
- ---
362
-
363
- ## Порядок реализации
364
-
365
- 1. **Sandbox snapshot** — добавить agent.js в image (30 мин)
366
- 2. **agent-runner.ts** — новый файл, запуск SDK через PTY (2-3 часа)
367
- 3. **route.ts v2** — переписать с agent-runner вместо LangGraph (4-6 часов)
368
- 4. **Event adapter** — маппинг SDK events → SSE events (1-2 часа)
369
- 5. **HITL bridge** — файловый transport через sandbox exec (1-2 часа)
370
- 6. **Frontend adapter** — activity events + minor event name changes (2-3 часа)
371
- 7. **Testing** — E2E: new chat → agent runs → tools work → HITL → plan mode (2-3 часа)
372
- 8. **Cleanup** — удалить LangGraph, patches, old tools (1 час)
373
-
374
- ---
375
-
376
- ## Риски и Mitigation
377
-
378
- | Риск | Mitigation |
379
- |------|-----------|
380
- | PTY output buffering (неполные JSON lines) | Буферизация в agent-runner: собирать строки до `\n` |
381
- | Sandbox timeout (agent работает долго) | `autoStopInterval: 60` + heartbeat через PTY |
382
- | HITL latency | PTY stdin — мгновенно, не нужен exec call |
383
- | Agent crash in sandbox | PTY exit code ≠ 0 → SSE "run-finished" с error |
384
- | Concurrent stdin + stdout | PTY handles both natively, no race condition |
385
- | SDK version mismatch | Embed version in agent.js, check on start |
package/build.ts DELETED
@@ -1,14 +0,0 @@
1
- import { build } from "esbuild";
2
-
3
- await build({
4
- entryPoints: ["src/index.ts"],
5
- bundle: true,
6
- platform: "node",
7
- target: "node20",
8
- format: "esm",
9
- outfile: "dist/agent.js",
10
- external: ["fsevents"],
11
- minify: true,
12
- sourcemap: true,
13
- banner: { js: "#!/usr/bin/env node" },
14
- });
package/hello.txt DELETED
@@ -1,4 +0,0 @@
1
- Hello World!
2
- This is a test file with multiple lines.
3
- Line 2.
4
- Line 3.
package/run.sh DELETED
@@ -1,84 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Strayl Agent Runner
4
- # Usage: ./run.sh "your prompt here"
5
- # Options:
6
- # ./run.sh "prompt" - raw JSON output
7
- # ./run.sh "prompt" --pretty - human-readable output
8
- # ./run.sh "prompt" --model deep - use specific model (auto|light|pro|deep)
9
- # ./run.sh "prompt" --work-dir /path - set working directory
10
-
11
- # Direct mode: SDK calls providers directly with API keys (for local dev only)
12
- # In production, STRAYL_LLM_DIRECT is NOT set — all calls go through api.strayl.dev
13
- export STRAYL_LLM_DIRECT="1"
14
- export GOOGLE_GENERATIVE_AI_API_KEY="AIzaSyBLeK4sA6RjoAk1f1011nvtaS0OxFO6nas"
15
-
16
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
17
- PROMPT=""
18
- MODEL="auto"
19
- PRETTY=false
20
- EXTRA_ARGS=()
21
-
22
- while [[ $# -gt 0 ]]; do
23
- case $1 in
24
- --pretty) PRETTY=true; shift ;;
25
- --model) MODEL="$2"; shift 2 ;;
26
- --work-dir|--blocked-tools|--max-iterations|--extra-prompt-file|--skills-dir)
27
- EXTRA_ARGS+=("$1" "$2"); shift 2 ;;
28
- *)
29
- if [[ -z "$PROMPT" ]]; then
30
- PROMPT="$1"
31
- fi
32
- shift ;;
33
- esac
34
- done
35
-
36
- if [[ -z "$PROMPT" ]]; then
37
- echo "Usage: ./run.sh \"your prompt\" [--pretty] [--model auto|light|pro|deep]"
38
- exit 1
39
- fi
40
-
41
- if $PRETTY; then
42
- node "$SCRIPT_DIR/dist/agent.js" --model "$MODEL" --prompt "$PROMPT" "${EXTRA_ARGS[@]}" 2>&1 | while IFS= read -r line; do
43
- type=$(echo "$line" | jq -r '.type // empty' 2>/dev/null)
44
- case "$type" in
45
- session-start)
46
- model=$(echo "$line" | jq -r '.model')
47
- echo -e "\033[36m▶ Session started (model: $model)\033[0m"
48
- ;;
49
- text-delta)
50
- echo -n "$(echo "$line" | jq -r '.text')"
51
- ;;
52
- reasoning-delta)
53
- echo -ne "\033[2m$(echo "$line" | jq -r '.text')\033[0m"
54
- ;;
55
- tool-call-start)
56
- name=$(echo "$line" | jq -r '.name')
57
- args=$(echo "$line" | jq -c '.args')
58
- echo -e "\n\033[33m🔧 $name\033[0m $args"
59
- ;;
60
- tool-result)
61
- echo -e "\033[32m✅ done\033[0m"
62
- ;;
63
- exec-log)
64
- echo -ne "\033[2m$(echo "$line" | jq -r '.data')\033[0m"
65
- ;;
66
- usage-update)
67
- left=$(echo "$line" | jq -r '.context_left_percent')
68
- cost=$(echo "$line" | jq -r '.cost // 0')
69
- ;;
70
- session-end)
71
- reason=$(echo "$line" | jq -r '.exit_reason')
72
- input=$(echo "$line" | jq -r '.usage.input_tokens')
73
- output=$(echo "$line" | jq -r '.usage.output_tokens')
74
- echo -e "\n\033[36m■ Done ($reason) | tokens: ${input}in/${output}out\033[0m"
75
- ;;
76
- error)
77
- msg=$(echo "$line" | jq -r '.message')
78
- echo -e "\033[31m✖ $msg\033[0m"
79
- ;;
80
- esac
81
- done
82
- else
83
- node "$SCRIPT_DIR/dist/agent.js" --model "$MODEL" --prompt "$PROMPT" "${EXTRA_ARGS[@]}" 2>&1
84
- fi
package/test-hitl.sh DELETED
@@ -1,90 +0,0 @@
1
- #!/bin/bash
2
- # Test HITL flow: agent asks a question → we respond via file → agent continues
3
- # Auto-responds to ALL HITL requests (agent may ask multiple questions)
4
-
5
- set -e
6
-
7
- HITL_DIR="/tmp/hitl-test-$$"
8
- mkdir -p "$HITL_DIR"
9
- rm -f "$HITL_DIR"/*
10
-
11
- echo "=== HITL Test: askUser ==="
12
- echo "HITL dir: $HITL_DIR"
13
- echo ""
14
-
15
- OUTPUT_FILE="/tmp/hitl-test-output-$$"
16
-
17
- STRAYL_LLM_DIRECT=1 \
18
- GOOGLE_GENERATIVE_AI_API_KEY="AIzaSyBLeK4sA6RjoAk1f1011nvtaS0OxFO6nas" \
19
- node dist/agent.js \
20
- --model auto \
21
- --mode normal \
22
- --hitl-dir "$HITL_DIR" \
23
- --max-iterations 10 \
24
- --prompt "Ask me what color I want for the website background. Use the askUser tool. After I answer, just say the color back to me and stop." \
25
- > "$OUTPUT_FILE" 2>&1 &
26
-
27
- AGENT_PID=$!
28
- echo "Agent PID: $AGENT_PID"
29
-
30
- # Background responder: watches for HITL requests and auto-answers them
31
- RESPONDED=""
32
- (
33
- while kill -0 $AGENT_PID 2>/dev/null; do
34
- # Check for new hitl-request events
35
- if [ -f "$OUTPUT_FILE" ]; then
36
- for SAFE_ID in $(grep '"hitl-request"' "$OUTPUT_FILE" 2>/dev/null | jq -r '.safe_id' 2>/dev/null); do
37
- # Skip already responded
38
- if echo "$RESPONDED" | grep -q "$SAFE_ID"; then
39
- continue
40
- fi
41
- RESPONDED="$RESPONDED $SAFE_ID"
42
-
43
- TOOL=$(grep "\"safe_id\":\"$SAFE_ID\"" "$OUTPUT_FILE" | jq -r '.tool' 2>/dev/null)
44
- echo "[AUTO-RESPOND] safe_id=$SAFE_ID tool=$TOOL → answer: blue"
45
-
46
- RESPONSE='{"decision":"edit","data":{"answer":"blue"}}'
47
- echo "$RESPONSE" > "$HITL_DIR/${SAFE_ID}.tmp"
48
- mv "$HITL_DIR/${SAFE_ID}.tmp" "$HITL_DIR/${SAFE_ID}.json"
49
- done
50
- fi
51
- sleep 0.3
52
- done
53
- ) &
54
- RESPONDER_PID=$!
55
-
56
- # Wait for agent to finish (max 90s)
57
- SECONDS=0
58
- while kill -0 $AGENT_PID 2>/dev/null && [ $SECONDS -lt 90 ]; do
59
- sleep 1
60
- done
61
-
62
- kill $AGENT_PID 2>/dev/null || true
63
- kill $RESPONDER_PID 2>/dev/null || true
64
- wait $AGENT_PID 2>/dev/null || true
65
- wait $RESPONDER_PID 2>/dev/null || true
66
-
67
- echo ""
68
- echo "=== Agent Output (pretty) ==="
69
- cat "$OUTPUT_FILE" | while IFS= read -r line; do
70
- type=$(echo "$line" | jq -r '.type // empty' 2>/dev/null)
71
- case "$type" in
72
- session-start) echo "[START] model=$(echo "$line" | jq -r '.model')" ;;
73
- text-delta) echo -n "$(echo "$line" | jq -r '.text')" ;;
74
- tool-call-start) echo -e "\n[TOOL] $(echo "$line" | jq -r '.name') $(echo "$line" | jq -c '.args')" ;;
75
- tool-result) echo "[RESULT] $(echo "$line" | jq -r '.output' | head -c 200)" ;;
76
- hitl-request) echo "[HITL-REQ] safe_id=$(echo "$line" | jq -r '.safe_id') tool=$(echo "$line" | jq -r '.tool') q=$(echo "$line" | jq -r '.args.question // empty')" ;;
77
- hitl-response) echo "[HITL-RES] decision=$(echo "$line" | jq -r '.decision')" ;;
78
- session-end) echo -e "\n[END] reason=$(echo "$line" | jq -r '.exit_reason')" ;;
79
- error) echo "[ERROR] $(echo "$line" | jq -r '.message')" ;;
80
- mode-changed) echo "[MODE] $(echo "$line" | jq -r '.from') -> $(echo "$line" | jq -r '.to')" ;;
81
- usage-update|checkpoint-saved|reasoning-delta) ;; # skip
82
- *) ;;
83
- esac
84
- done
85
-
86
- echo ""
87
- echo "=== Test Complete ==="
88
-
89
- # Cleanup
90
- rm -rf "$HITL_DIR" "$OUTPUT_FILE"
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "outDir": "dist",
10
- "rootDir": "src",
11
- "declaration": true,
12
- "sourceMap": true
13
- },
14
- "include": ["src"]
15
- }