@poncho-ai/harness 0.20.13 → 0.21.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/.turbo/turbo-build.log +7 -6
- package/CHANGELOG.md +14 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1175 -1
- package/package.json +3 -3
- package/scripts/embed-docs.js +26 -0
- package/src/config.ts +5 -0
- package/src/default-tools.ts +31 -1
- package/src/harness.ts +13 -2
- package/.turbo/turbo-lint.log +0 -6
- package/.turbo/turbo-test.log +0 -135
package/dist/index.js
CHANGED
|
@@ -406,6 +406,1143 @@ var loadPonchoConfig = async (workingDir) => {
|
|
|
406
406
|
import { mkdir, readdir, readFile as readFile3, rm, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
407
407
|
import { dirname, resolve as resolve4, sep } from "path";
|
|
408
408
|
import { defineTool } from "@poncho-ai/sdk";
|
|
409
|
+
|
|
410
|
+
// src/generated/poncho-docs.ts
|
|
411
|
+
var PONCHO_DOCS = {
|
|
412
|
+
"api": `# HTTP API & Client SDK
|
|
413
|
+
|
|
414
|
+
> Back to [README](../README.md)
|
|
415
|
+
|
|
416
|
+
Your deployed agent exposes these endpoints:
|
|
417
|
+
|
|
418
|
+
| Endpoint | Use case |
|
|
419
|
+
|----------|----------|
|
|
420
|
+
| \`GET /health\` | Health checks for load balancers |
|
|
421
|
+
| \`GET /api/docs\` | Interactive API documentation (Scalar) |
|
|
422
|
+
| \`GET /api/openapi.json\` | OpenAPI 3.1 spec (machine-readable) |
|
|
423
|
+
| \`GET /api/auth/session\` | Session status + CSRF token for browser clients |
|
|
424
|
+
| \`POST /api/auth/login\` | Passphrase login for browser sessions |
|
|
425
|
+
| \`POST /api/auth/logout\` | End browser session |
|
|
426
|
+
| \`GET /api/conversations\` | List stored conversations |
|
|
427
|
+
| \`POST /api/conversations\` | Create conversation |
|
|
428
|
+
| \`GET /api/conversations/:conversationId\` | Get conversation transcript |
|
|
429
|
+
| \`PATCH /api/conversations/:conversationId\` | Rename conversation |
|
|
430
|
+
| \`DELETE /api/conversations/:conversationId\` | Delete conversation |
|
|
431
|
+
| \`POST /api/conversations/:conversationId/messages\` | Stream a new assistant response |
|
|
432
|
+
| \`GET /api/conversations/:conversationId/events\` | Attach to live SSE stream |
|
|
433
|
+
| \`POST /api/conversations/:conversationId/stop\` | Cancel an in-flight run |
|
|
434
|
+
| \`POST /api/approvals/:approvalId\` | Resolve tool approval request |
|
|
435
|
+
| \`GET /api/uploads/:key\` | Retrieve uploaded file |
|
|
436
|
+
| \`GET\\|POST /api/cron/:jobName\` | Trigger a cron job (see [Cron Jobs](../README.md#cron-jobs)) |
|
|
437
|
+
| \`GET /api/browser/status\` | Browser session status (when browser is enabled) |
|
|
438
|
+
| \`GET /api/browser/stream\` | SSE stream of live browser viewport frames |
|
|
439
|
+
| \`POST /api/browser/input\` | Send input events to the browser session |
|
|
440
|
+
| \`POST /api/browser/navigate\` | Navigate the browser to a URL |
|
|
441
|
+
|
|
442
|
+
> **Tip:** Visit \`/api/docs\` on any running agent for interactive API documentation with request examples and full schema details.
|
|
443
|
+
|
|
444
|
+
## POST /api/conversations/:conversationId/messages (streaming)
|
|
445
|
+
|
|
446
|
+
\`\`\`bash
|
|
447
|
+
curl -N -X POST https://my-agent.vercel.app/api/conversations/<conversation-id>/messages \\
|
|
448
|
+
-H "Content-Type: application/json" \\
|
|
449
|
+
-d '{"message": "Write a hello world function"}'
|
|
450
|
+
\`\`\`
|
|
451
|
+
|
|
452
|
+
Response: Server-Sent Events (\`run:started\`, \`model:chunk\`, \`tool:*\`, \`run:completed\`).
|
|
453
|
+
|
|
454
|
+
On serverless deployments with \`PONCHO_MAX_DURATION\` set, the \`run:completed\` event may
|
|
455
|
+
include \`continuation: true\` in \`result\`, indicating the agent stopped early due to a
|
|
456
|
+
platform timeout and the client should send another message (e.g., \`"Continue"\`) on the
|
|
457
|
+
same conversation to resume.
|
|
458
|
+
|
|
459
|
+
## Build a custom chat UI
|
|
460
|
+
|
|
461
|
+
You can build your own chat frontend by calling Poncho's conversation endpoints directly.
|
|
462
|
+
|
|
463
|
+
Typical UI flow:
|
|
464
|
+
|
|
465
|
+
1. Create a conversation: \`POST /api/conversations\`
|
|
466
|
+
2. Send a message and stream events: \`POST /api/conversations/:conversationId/messages\`
|
|
467
|
+
3. Append \`model:chunk\` events into the in-progress assistant message
|
|
468
|
+
4. Render \`tool:*\` events as activity status
|
|
469
|
+
5. Finalize on \`run:completed\` (or handle \`run:error\` / \`run:cancelled\`)
|
|
470
|
+
6. Reload full transcript on refresh: \`GET /api/conversations/:conversationId\`
|
|
471
|
+
|
|
472
|
+
Minimal browser example (SSE parsing):
|
|
473
|
+
|
|
474
|
+
\`\`\`typescript
|
|
475
|
+
async function streamMessage(conversationId: string, message: string) {
|
|
476
|
+
const response = await fetch(
|
|
477
|
+
\`/api/conversations/\${encodeURIComponent(conversationId)}/messages\`,
|
|
478
|
+
{
|
|
479
|
+
method: "POST",
|
|
480
|
+
headers: { "Content-Type": "application/json" },
|
|
481
|
+
body: JSON.stringify({ message }),
|
|
482
|
+
credentials: "include", // keep for session auth
|
|
483
|
+
},
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
if (!response.ok || !response.body) {
|
|
487
|
+
throw new Error(\`Streaming request failed: HTTP \${response.status}\`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const reader = response.body.getReader();
|
|
491
|
+
const decoder = new TextDecoder();
|
|
492
|
+
let buffer = "";
|
|
493
|
+
|
|
494
|
+
while (true) {
|
|
495
|
+
const { done, value } = await reader.read();
|
|
496
|
+
if (done) break;
|
|
497
|
+
|
|
498
|
+
buffer += decoder.decode(value, { stream: true });
|
|
499
|
+
const frames = buffer.split("\\n\\n");
|
|
500
|
+
buffer = frames.pop() ?? "";
|
|
501
|
+
|
|
502
|
+
for (const frame of frames) {
|
|
503
|
+
const lines = frame
|
|
504
|
+
.split("\\n")
|
|
505
|
+
.map((line) => line.trim())
|
|
506
|
+
.filter(Boolean);
|
|
507
|
+
|
|
508
|
+
const eventLine = lines.find((line) => line.startsWith("event:"));
|
|
509
|
+
const dataLine = lines.find((line) => line.startsWith("data:"));
|
|
510
|
+
if (!eventLine || !dataLine) continue;
|
|
511
|
+
|
|
512
|
+
const eventName = eventLine.slice("event:".length).trim();
|
|
513
|
+
const payload = JSON.parse(dataLine.slice("data:".length).trim());
|
|
514
|
+
|
|
515
|
+
if (eventName === "model:chunk") {
|
|
516
|
+
// Append payload.content to the active assistant message
|
|
517
|
+
} else if (eventName === "tool:started") {
|
|
518
|
+
// Show "running tool" activity
|
|
519
|
+
} else if (eventName === "tool:completed") {
|
|
520
|
+
// Mark tool activity as complete
|
|
521
|
+
} else if (eventName === "run:completed") {
|
|
522
|
+
// Finalize assistant message
|
|
523
|
+
} else if (eventName === "run:error" || eventName === "run:cancelled") {
|
|
524
|
+
// Show interrupted/error state in UI
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
\`\`\`
|
|
530
|
+
|
|
531
|
+
Useful optional endpoints for richer UIs:
|
|
532
|
+
|
|
533
|
+
- \`POST /api/conversations/:conversationId/stop\` with \`{ "runId": "<run-id>" }\` to cancel an in-flight run
|
|
534
|
+
- \`GET /api/conversations/:conversationId/events\` to attach/re-attach to a live event stream
|
|
535
|
+
- \`POST /api/approvals/:approvalId\` with \`{ "approved": true|false }\` to resolve \`tool:approval:required\`
|
|
536
|
+
|
|
537
|
+
Auth notes for custom frontends:
|
|
538
|
+
|
|
539
|
+
- Browser session mode: \`GET /api/auth/session\`, then \`POST /api/auth/login\`, and send \`x-csrf-token\` on mutating requests.
|
|
540
|
+
- API token mode: send \`Authorization: Bearer <PONCHO_AUTH_TOKEN>\` on API requests.
|
|
541
|
+
|
|
542
|
+
## Headless mode (API-only)
|
|
543
|
+
|
|
544
|
+
If you're building your own frontend or using the agent purely as an API, disable the built-in Web UI:
|
|
545
|
+
|
|
546
|
+
\`\`\`javascript
|
|
547
|
+
// poncho.config.js
|
|
548
|
+
export default {
|
|
549
|
+
webUi: false,
|
|
550
|
+
}
|
|
551
|
+
\`\`\`
|
|
552
|
+
|
|
553
|
+
When \`webUi\` is \`false\`:
|
|
554
|
+
- The built-in chat UI at \`/\` is disabled (returns 404).
|
|
555
|
+
- All \`/api/*\` endpoints, \`/health\`, and \`/api/docs\` continue to work normally.
|
|
556
|
+
- Messaging adapter routes (e.g., Slack) are unaffected.
|
|
557
|
+
|
|
558
|
+
This is useful for API-only deployments where a separate frontend (e.g., a Next.js app) calls the Poncho API via a backend-for-frontend pattern.
|
|
559
|
+
|
|
560
|
+
## TypeScript/JavaScript Client
|
|
561
|
+
|
|
562
|
+
Install the client SDK for type-safe access:
|
|
563
|
+
|
|
564
|
+
\`\`\`bash
|
|
565
|
+
npm install @poncho-ai/client
|
|
566
|
+
\`\`\`
|
|
567
|
+
|
|
568
|
+
\`\`\`typescript
|
|
569
|
+
import { AgentClient } from '@poncho-ai/client'
|
|
570
|
+
|
|
571
|
+
const agent = new AgentClient({
|
|
572
|
+
url: 'https://my-agent.vercel.app',
|
|
573
|
+
apiKey: 'your-api-key' // Optional, if auth enabled
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
// Create and send in one call (conversation() returns a stateful helper)
|
|
577
|
+
const conv = agent.conversation()
|
|
578
|
+
const first = await conv.send('What is 2 + 2?')
|
|
579
|
+
console.log(first.result.response)
|
|
580
|
+
const followUp = await conv.send('And what is 3 + 3?')
|
|
581
|
+
|
|
582
|
+
// Or manage conversations explicitly
|
|
583
|
+
const created = await agent.createConversation({ title: 'Session' })
|
|
584
|
+
const response = await agent.sendMessage(created.conversationId, 'What is 2 + 2?')
|
|
585
|
+
console.log(response.result.response)
|
|
586
|
+
|
|
587
|
+
// Send with file attachments
|
|
588
|
+
await agent.sendMessage(created.conversationId, 'Describe this image', {
|
|
589
|
+
files: [{ data: base64Data, mediaType: 'image/png', filename: 'photo.png' }],
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
// One-shot run (creates a conversation automatically)
|
|
593
|
+
const result = await agent.run({ task: 'Write a haiku about coding' })
|
|
594
|
+
|
|
595
|
+
// Stream events (creates a conversation automatically)
|
|
596
|
+
for await (const event of agent.stream({ task: 'Write a story' })) {
|
|
597
|
+
if (event.type === 'model:chunk') process.stdout.write(event.content)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// List, get, delete conversations
|
|
601
|
+
const conversations = await agent.listConversations()
|
|
602
|
+
const transcript = await agent.getConversation(created.conversationId)
|
|
603
|
+
await agent.deleteConversation(created.conversationId)
|
|
604
|
+
\`\`\`
|
|
605
|
+
|
|
606
|
+
## Multi-turn Conversations
|
|
607
|
+
|
|
608
|
+
Conversations are persisted and keyed by \`conversationId\`.
|
|
609
|
+
|
|
610
|
+
Typical flow:
|
|
611
|
+
|
|
612
|
+
1. \`POST /api/conversations\`
|
|
613
|
+
2. \`POST /api/conversations/:conversationId/messages\`
|
|
614
|
+
3. Repeat step 2 for follow-up turns
|
|
615
|
+
4. \`GET /api/conversations/:conversationId\` to fetch full transcript
|
|
616
|
+
|
|
617
|
+
## File Attachments
|
|
618
|
+
|
|
619
|
+
Agents support multimodal inputs \u2014 attach files to any message via the Web UI, API, or client SDK.
|
|
620
|
+
|
|
621
|
+
### Web UI
|
|
622
|
+
|
|
623
|
+
Click the attach button (paperclip icon), drag files onto the chat, or paste from your clipboard. Attached files appear as previews above the composer before sending.
|
|
624
|
+
|
|
625
|
+
### HTTP API
|
|
626
|
+
|
|
627
|
+
Send files as \`multipart/form-data\` or as base64-encoded JSON:
|
|
628
|
+
|
|
629
|
+
\`\`\`bash
|
|
630
|
+
# multipart/form-data
|
|
631
|
+
curl -N -X POST "http://localhost:3000/api/conversations/<id>/messages" \\
|
|
632
|
+
-F "message=Describe this image" \\
|
|
633
|
+
-F "files=@screenshot.png"
|
|
634
|
+
|
|
635
|
+
# JSON with base64
|
|
636
|
+
curl -N -X POST "http://localhost:3000/api/conversations/<id>/messages" \\
|
|
637
|
+
-H "Content-Type: application/json" \\
|
|
638
|
+
-d '{
|
|
639
|
+
"message": "Describe this image",
|
|
640
|
+
"files": [{
|
|
641
|
+
"data": "<base64-encoded>",
|
|
642
|
+
"mediaType": "image/png",
|
|
643
|
+
"filename": "screenshot.png"
|
|
644
|
+
}]
|
|
645
|
+
}'
|
|
646
|
+
\`\`\`
|
|
647
|
+
|
|
648
|
+
### Client SDK
|
|
649
|
+
|
|
650
|
+
\`\`\`typescript
|
|
651
|
+
const response = await agent.sendMessage(conversationId, 'Describe this image', {
|
|
652
|
+
files: [{ data: base64Data, mediaType: 'image/png', filename: 'screenshot.png' }],
|
|
653
|
+
})
|
|
654
|
+
\`\`\`
|
|
655
|
+
|
|
656
|
+
### Upload storage
|
|
657
|
+
|
|
658
|
+
By default, uploaded files are stored on the local filesystem. For production deployments, configure a cloud upload provider:
|
|
659
|
+
|
|
660
|
+
\`\`\`javascript
|
|
661
|
+
// poncho.config.js
|
|
662
|
+
export default {
|
|
663
|
+
uploads: {
|
|
664
|
+
provider: 'vercel-blob', // 'local' | 'vercel-blob' | 's3'
|
|
665
|
+
access: 'public', // vercel-blob access mode (default: 'public')
|
|
666
|
+
},
|
|
667
|
+
// Or S3-compatible storage:
|
|
668
|
+
uploads: {
|
|
669
|
+
provider: 's3',
|
|
670
|
+
bucket: 'my-agent-uploads',
|
|
671
|
+
region: 'us-east-1',
|
|
672
|
+
endpoint: 'https://s3.amazonaws.com', // optional, for S3-compatible services
|
|
673
|
+
},
|
|
674
|
+
}
|
|
675
|
+
\`\`\`
|
|
676
|
+
|
|
677
|
+
Environment variables for upload providers:
|
|
678
|
+
|
|
679
|
+
| Provider | Required env vars |
|
|
680
|
+
|----------|-------------------|
|
|
681
|
+
| \`vercel-blob\` | \`BLOB_READ_WRITE_TOKEN\` |
|
|
682
|
+
| \`s3\` | \`AWS_ACCESS_KEY_ID\`, \`AWS_SECRET_ACCESS_KEY\`, \`PONCHO_UPLOADS_BUCKET\` (or \`uploads.bucket\` in config) |
|
|
683
|
+
`,
|
|
684
|
+
"features": `# Platform Features
|
|
685
|
+
|
|
686
|
+
> Back to [README](../README.md)
|
|
687
|
+
|
|
688
|
+
## Web UI
|
|
689
|
+
|
|
690
|
+
The built-in web UI at \`http://localhost:3000\` provides a full-featured chat interface:
|
|
691
|
+
|
|
692
|
+
- **Conversation sidebar**: create, switch between, rename, and delete conversations. Each conversation has a persistent URL (\`/c/:conversationId\`).
|
|
693
|
+
- **Streaming responses**: assistant text and tool activity stream in real time with structured sections (text blocks, tool call groups).
|
|
694
|
+
- **Context window progress**: a circular ring around the send button shows how much of the model's context window is used. The ring updates as the model responds and as tool results come in, with warning (70%) and critical (90%) color thresholds. Context usage is persisted per conversation so the ring stays accurate when switching between conversations.
|
|
695
|
+
- **File attachments**: attach images, PDFs, text files, and more via the attach button, drag-and-drop, or clipboard paste. Supported types include images, video, PDF, CSV, JSON, HTML, and plain text (up to 25 MB each).
|
|
696
|
+
- **Image lightbox**: click any image in the chat to view it full-size.
|
|
697
|
+
- **Tool approval**: when a tool requires approval, the UI shows an inline Approve/Deny prompt.
|
|
698
|
+
- **Stop streaming**: click the send button while a response is streaming to cancel the current run.
|
|
699
|
+
- **Installable PWA**: the web UI includes a manifest and service worker, so it can be installed as a standalone app on desktop and mobile.
|
|
700
|
+
|
|
701
|
+
Disable the built-in UI for API-only deployments by setting \`webUi: false\` in \`poncho.config.js\`.
|
|
702
|
+
|
|
703
|
+
## Messaging Integrations
|
|
704
|
+
|
|
705
|
+
Connect your Poncho agent to messaging platforms so it responds to @mentions.
|
|
706
|
+
|
|
707
|
+
### Slack
|
|
708
|
+
|
|
709
|
+
#### 1. Create a Slack App
|
|
710
|
+
|
|
711
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps) and create a new app "From scratch"
|
|
712
|
+
2. Under **OAuth & Permissions**, add these Bot Token Scopes:
|
|
713
|
+
- \`app_mentions:read\`
|
|
714
|
+
- \`chat:write\`
|
|
715
|
+
- \`reactions:write\`
|
|
716
|
+
3. Under **Event Subscriptions**, enable events:
|
|
717
|
+
- Set the Request URL to \`https://<your-deployed-agent>/api/messaging/slack\`
|
|
718
|
+
- Subscribe to the \`app_mention\` bot event
|
|
719
|
+
4. Install the app to your workspace (generates a Bot Token \`xoxb-...\`)
|
|
720
|
+
5. Copy the **Signing Secret** from the Basic Information page
|
|
721
|
+
|
|
722
|
+
#### 2. Configure your agent
|
|
723
|
+
|
|
724
|
+
Add environment variables to \`.env\`:
|
|
725
|
+
|
|
726
|
+
\`\`\`
|
|
727
|
+
SLACK_BOT_TOKEN=xoxb-...
|
|
728
|
+
SLACK_SIGNING_SECRET=...
|
|
729
|
+
\`\`\`
|
|
730
|
+
|
|
731
|
+
Add messaging to \`poncho.config.js\`:
|
|
732
|
+
|
|
733
|
+
\`\`\`javascript
|
|
734
|
+
export default {
|
|
735
|
+
// ... your existing config ...
|
|
736
|
+
messaging: [
|
|
737
|
+
{ platform: 'slack' }
|
|
738
|
+
]
|
|
739
|
+
}
|
|
740
|
+
\`\`\`
|
|
741
|
+
|
|
742
|
+
#### 3. Deploy
|
|
743
|
+
|
|
744
|
+
The messaging endpoint works on all deployment targets (Vercel, Docker, Fly.io, etc.). For local development with Slack, use a tunnel like [ngrok](https://ngrok.com) to expose your local server.
|
|
745
|
+
|
|
746
|
+
**Vercel deployments:** install \`@vercel/functions\` so Poncho can keep the serverless function alive while the agent processes messages:
|
|
747
|
+
|
|
748
|
+
\`\`\`bash
|
|
749
|
+
npm install @vercel/functions
|
|
750
|
+
\`\`\`
|
|
751
|
+
|
|
752
|
+
This is detected automatically at runtime -- no extra configuration needed.
|
|
753
|
+
|
|
754
|
+
#### How it works
|
|
755
|
+
|
|
756
|
+
- When a user @mentions your bot in Slack, the agent receives the message and responds in the same thread.
|
|
757
|
+
- Each Slack thread maps to a separate Poncho conversation with persistent history.
|
|
758
|
+
- The bot adds an "eyes" reaction while processing and removes it when done.
|
|
759
|
+
- Long responses are automatically split into multiple messages.
|
|
760
|
+
|
|
761
|
+
#### Custom environment variable names
|
|
762
|
+
|
|
763
|
+
If you need different env var names (e.g., running multiple Slack integrations):
|
|
764
|
+
|
|
765
|
+
\`\`\`javascript
|
|
766
|
+
messaging: [
|
|
767
|
+
{
|
|
768
|
+
platform: 'slack',
|
|
769
|
+
botTokenEnv: 'MY_SLACK_BOT_TOKEN',
|
|
770
|
+
signingSecretEnv: 'MY_SLACK_SIGNING_SECRET',
|
|
771
|
+
}
|
|
772
|
+
]
|
|
773
|
+
\`\`\`
|
|
774
|
+
|
|
775
|
+
### Telegram
|
|
776
|
+
|
|
777
|
+
#### 1. Create a Telegram Bot
|
|
778
|
+
|
|
779
|
+
1. Open [Telegram](https://telegram.org) and start a chat with [@BotFather](https://t.me/BotFather)
|
|
780
|
+
2. Send \`/newbot\` and follow the prompts to create your bot
|
|
781
|
+
3. Copy the **Bot Token** (looks like \`123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11\`)
|
|
782
|
+
|
|
783
|
+
> **Privacy mode** is enabled by default, which means the bot only receives messages that @mention it in groups. This is the desired behavior -- no changes needed.
|
|
784
|
+
|
|
785
|
+
#### 2. Configure your agent
|
|
786
|
+
|
|
787
|
+
Add environment variables to \`.env\`:
|
|
788
|
+
|
|
789
|
+
\`\`\`
|
|
790
|
+
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
|
|
791
|
+
TELEGRAM_WEBHOOK_SECRET=my-secret-token
|
|
792
|
+
\`\`\`
|
|
793
|
+
|
|
794
|
+
The webhook secret is optional but recommended. It can be any string up to 256 characters (\`A-Z\`, \`a-z\`, \`0-9\`, \`_\`, \`-\`).
|
|
795
|
+
|
|
796
|
+
Add messaging to \`poncho.config.js\`:
|
|
797
|
+
|
|
798
|
+
\`\`\`javascript
|
|
799
|
+
export default {
|
|
800
|
+
// ... your existing config ...
|
|
801
|
+
messaging: [
|
|
802
|
+
{ platform: 'telegram' }
|
|
803
|
+
]
|
|
804
|
+
}
|
|
805
|
+
\`\`\`
|
|
806
|
+
|
|
807
|
+
#### 3. Set up the webhook
|
|
808
|
+
|
|
809
|
+
After deploying your agent, register the webhook URL with Telegram:
|
|
810
|
+
|
|
811
|
+
\`\`\`bash
|
|
812
|
+
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" \\
|
|
813
|
+
-H "Content-Type: application/json" \\
|
|
814
|
+
-d '{"url": "https://<your-deployed-agent>/api/messaging/telegram", "secret_token": "<YOUR_WEBHOOK_SECRET>"}'
|
|
815
|
+
\`\`\`
|
|
816
|
+
|
|
817
|
+
Omit the \`secret_token\` field if you're not using a webhook secret.
|
|
818
|
+
|
|
819
|
+
For local development, use a tunnel like [ngrok](https://ngrok.com) to expose your local server, then register the tunnel URL as the webhook.
|
|
820
|
+
|
|
821
|
+
#### 4. Deploy
|
|
822
|
+
|
|
823
|
+
The messaging endpoint works on all deployment targets (Vercel, Docker, Fly.io, etc.).
|
|
824
|
+
|
|
825
|
+
**Vercel deployments:** install \`@vercel/functions\` so Poncho can keep the serverless function alive while the agent processes messages:
|
|
826
|
+
|
|
827
|
+
\`\`\`bash
|
|
828
|
+
npm install @vercel/functions
|
|
829
|
+
\`\`\`
|
|
830
|
+
|
|
831
|
+
#### How it works
|
|
832
|
+
|
|
833
|
+
- **Private chats**: the bot responds to all messages.
|
|
834
|
+
- **Groups**: the bot only responds when @mentioned (e.g., \`@mybot what's the weather?\`). The mention is stripped before the message reaches the agent.
|
|
835
|
+
- **Forum topics**: each topic in a supergroup is treated as a separate conversation.
|
|
836
|
+
- The bot shows a "typing..." indicator while processing.
|
|
837
|
+
- Long responses are automatically split into multiple messages (4096 char limit).
|
|
838
|
+
- **Photos and documents** sent to the bot are forwarded to the agent as file attachments.
|
|
839
|
+
- Use \`/new\` to reset the conversation and start fresh. In groups, use \`/new@botusername\`.
|
|
840
|
+
|
|
841
|
+
#### Restricting access
|
|
842
|
+
|
|
843
|
+
By default any Telegram user can message your bot. To restrict it to specific users, add their numeric Telegram user IDs:
|
|
844
|
+
|
|
845
|
+
\`\`\`javascript
|
|
846
|
+
messaging: [
|
|
847
|
+
{
|
|
848
|
+
platform: 'telegram',
|
|
849
|
+
allowedUserIds: [1056240469, 9876543210],
|
|
850
|
+
}
|
|
851
|
+
]
|
|
852
|
+
\`\`\`
|
|
853
|
+
|
|
854
|
+
Messages from anyone not on the list are silently ignored. You can find your user ID by messaging [@userinfobot](https://t.me/userinfobot) on Telegram.
|
|
855
|
+
|
|
856
|
+
#### Custom environment variable names
|
|
857
|
+
|
|
858
|
+
If you need different env var names (e.g., running multiple Telegram integrations):
|
|
859
|
+
|
|
860
|
+
\`\`\`javascript
|
|
861
|
+
messaging: [
|
|
862
|
+
{
|
|
863
|
+
platform: 'telegram',
|
|
864
|
+
botTokenEnv: 'MY_TELEGRAM_BOT_TOKEN',
|
|
865
|
+
webhookSecretEnv: 'MY_TELEGRAM_WEBHOOK_SECRET',
|
|
866
|
+
}
|
|
867
|
+
]
|
|
868
|
+
\`\`\`
|
|
869
|
+
|
|
870
|
+
### Email (Resend)
|
|
871
|
+
|
|
872
|
+
#### 1. Set up Resend
|
|
873
|
+
|
|
874
|
+
1. Create an account at [resend.com](https://resend.com) and add your domain
|
|
875
|
+
2. Enable **Inbound** on your domain in the Resend dashboard
|
|
876
|
+
3. Create a **Webhook** subscribing to the \`email.received\` event
|
|
877
|
+
- Set the endpoint URL to \`https://<your-deployed-agent>/api/messaging/resend\`
|
|
878
|
+
4. Copy the webhook **Signing Secret** from the webhook details page
|
|
879
|
+
5. Create an API key at [resend.com/api-keys](https://resend.com/api-keys)
|
|
880
|
+
|
|
881
|
+
#### 2. Configure your agent
|
|
882
|
+
|
|
883
|
+
Add environment variables to \`.env\`:
|
|
884
|
+
|
|
885
|
+
\`\`\`
|
|
886
|
+
RESEND_API_KEY=re_...
|
|
887
|
+
RESEND_WEBHOOK_SECRET=whsec_...
|
|
888
|
+
RESEND_FROM=Agent <agent@yourdomain.com>
|
|
889
|
+
RESEND_REPLY_TO=support@yourdomain.com # optional
|
|
890
|
+
\`\`\`
|
|
891
|
+
|
|
892
|
+
Add messaging to \`poncho.config.js\`:
|
|
893
|
+
|
|
894
|
+
\`\`\`javascript
|
|
895
|
+
export default {
|
|
896
|
+
// ... your existing config ...
|
|
897
|
+
messaging: [
|
|
898
|
+
{ platform: 'resend' }
|
|
899
|
+
]
|
|
900
|
+
}
|
|
901
|
+
\`\`\`
|
|
902
|
+
|
|
903
|
+
Install the Resend SDK:
|
|
904
|
+
|
|
905
|
+
\`\`\`bash
|
|
906
|
+
npm install resend
|
|
907
|
+
\`\`\`
|
|
908
|
+
|
|
909
|
+
#### 3. Deploy
|
|
910
|
+
|
|
911
|
+
The messaging endpoint works on all deployment targets (Vercel, Docker, Fly.io, etc.). For local development, use a tunnel like [ngrok](https://ngrok.com) to expose your local server and set it as your Resend webhook URL.
|
|
912
|
+
|
|
913
|
+
**Vercel deployments:** install \`@vercel/functions\` so Poncho can keep the serverless function alive while the agent processes messages:
|
|
914
|
+
|
|
915
|
+
\`\`\`bash
|
|
916
|
+
npm install @vercel/functions
|
|
917
|
+
\`\`\`
|
|
918
|
+
|
|
919
|
+
#### How it works
|
|
920
|
+
|
|
921
|
+
- When someone emails your agent's address, the agent receives the message and replies in the same email thread.
|
|
922
|
+
- Each email thread maps to a separate Poncho conversation with persistent history.
|
|
923
|
+
- Email attachments are passed to the agent as file inputs.
|
|
924
|
+
- Agent responses are formatted as HTML emails with proper markdown rendering.
|
|
925
|
+
- Quoted reply content is automatically stripped so the agent only sees the new message.
|
|
926
|
+
- The incoming email's sender and subject are included in the task header (\`From:\` / \`Subject:\`) so the agent knows who sent the email.
|
|
927
|
+
|
|
928
|
+
#### Response modes
|
|
929
|
+
|
|
930
|
+
Resend email supports two response modes:
|
|
931
|
+
|
|
932
|
+
- **\`"auto-reply"\`** (default): The agent's text response is automatically sent back as an email reply. Simple and zero-config.
|
|
933
|
+
- **\`"tool"\`**: Auto-reply is disabled. Instead, the agent gets a \`send_email\` tool with full control over recipients, subject, body, CC/BCC, and threading. Use this for agents that need to send emails to different people, compose custom subjects, or decide whether to reply at all.
|
|
934
|
+
|
|
935
|
+
#### Options
|
|
936
|
+
|
|
937
|
+
\`\`\`javascript
|
|
938
|
+
messaging: [
|
|
939
|
+
{
|
|
940
|
+
platform: 'resend',
|
|
941
|
+
// Optional: restrict who can email the agent
|
|
942
|
+
allowedSenders: ['*@mycompany.com', 'partner@external.com'],
|
|
943
|
+
// Optional: custom env var names
|
|
944
|
+
apiKeyEnv: 'MY_RESEND_API_KEY',
|
|
945
|
+
webhookSecretEnv: 'MY_RESEND_WEBHOOK_SECRET',
|
|
946
|
+
fromEnv: 'MY_RESEND_FROM',
|
|
947
|
+
replyToEnv: 'MY_RESEND_REPLY_TO',
|
|
948
|
+
}
|
|
949
|
+
]
|
|
950
|
+
\`\`\`
|
|
951
|
+
|
|
952
|
+
**Tool mode** gives the agent explicit email control:
|
|
953
|
+
|
|
954
|
+
\`\`\`javascript
|
|
955
|
+
messaging: [
|
|
956
|
+
{
|
|
957
|
+
platform: 'resend',
|
|
958
|
+
mode: 'tool',
|
|
959
|
+
// Optional: restrict who the agent can email (glob patterns)
|
|
960
|
+
allowedRecipients: ['*@mycompany.com', 'partner@external.com'],
|
|
961
|
+
// Optional: max emails per agent run (default: 10)
|
|
962
|
+
maxSendsPerRun: 5,
|
|
963
|
+
}
|
|
964
|
+
]
|
|
965
|
+
\`\`\`
|
|
966
|
+
|
|
967
|
+
In tool mode the agent can call \`send_email\` with:
|
|
968
|
+
- \`to\` (required): recipient email addresses
|
|
969
|
+
- \`subject\` (required): email subject
|
|
970
|
+
- \`body\` (required): markdown content (auto-converted to HTML)
|
|
971
|
+
- \`cc\`, \`bcc\` (optional): additional recipients
|
|
972
|
+
- \`in_reply_to\` (optional): message ID for threading as a reply; omit for a standalone email
|
|
973
|
+
|
|
974
|
+
### Custom Messaging Adapters
|
|
975
|
+
|
|
976
|
+
The \`MessagingAdapter\` interface from \`@poncho-ai/messaging\` is the extension point for adding other messaging platforms (SendGrid, Postmark, Discord, etc.). Implement the interface and wire it with \`AgentBridge\`:
|
|
977
|
+
|
|
978
|
+
\`\`\`typescript
|
|
979
|
+
import { AgentBridge, type MessagingAdapter } from '@poncho-ai/messaging';
|
|
980
|
+
// Shared email utilities for threading (optional, useful for email adapters)
|
|
981
|
+
import { parseReferences, deriveRootMessageId, buildReplyHeaders } from '@poncho-ai/messaging';
|
|
982
|
+
\`\`\`
|
|
983
|
+
|
|
984
|
+
See the \`SlackAdapter\` and \`ResendAdapter\` source code for reference implementations.
|
|
985
|
+
|
|
986
|
+
## Browser Automation (Experimental)
|
|
987
|
+
|
|
988
|
+
Give your agent the ability to browse the web with a headless Chromium browser. Powered by [\`agent-browser\`](https://github.com/vercel-labs/agent-browser).
|
|
989
|
+
|
|
990
|
+
\`\`\`javascript
|
|
991
|
+
// poncho.config.js
|
|
992
|
+
export default {
|
|
993
|
+
browser: true,
|
|
994
|
+
// or with options:
|
|
995
|
+
browser: {
|
|
996
|
+
viewport: { width: 1280, height: 720 },
|
|
997
|
+
quality: 80,
|
|
998
|
+
everyNthFrame: 2,
|
|
999
|
+
headless: true,
|
|
1000
|
+
profileDir: "~/.poncho/browser-profiles",
|
|
1001
|
+
stealth: true, // Anti-bot-detection (default: true)
|
|
1002
|
+
userAgent: "custom UA", // Override the default stealth user-agent
|
|
1003
|
+
},
|
|
1004
|
+
}
|
|
1005
|
+
\`\`\`
|
|
1006
|
+
|
|
1007
|
+
When \`browser\` is enabled, the agent gets eight tools:
|
|
1008
|
+
|
|
1009
|
+
- \`browser_open\` \u2014 Navigate to a URL. Starts real-time viewport streaming.
|
|
1010
|
+
- \`browser_snapshot\` \u2014 Get the page as a compact accessibility tree with element refs (\`@e1\`, \`@e2\`, ...).
|
|
1011
|
+
- \`browser_click\` \u2014 Click an element by ref.
|
|
1012
|
+
- \`browser_type\` \u2014 Type text into a form field by ref.
|
|
1013
|
+
- \`browser_content\` \u2014 Get the visible text content of the current page as plain text.
|
|
1014
|
+
- \`browser_screenshot\` \u2014 Take a PNG screenshot (sent to the model as an image).
|
|
1015
|
+
- \`browser_scroll\` \u2014 Scroll the page up or down.
|
|
1016
|
+
- \`browser_close\` \u2014 Save session and close the browser.
|
|
1017
|
+
|
|
1018
|
+
The agent uses the snapshot/ref pattern: call \`browser_snapshot\` to get refs, then \`browser_click @e2\` or \`browser_type @e3 "hello"\`. Re-snapshot after each interaction since refs change when the page updates.
|
|
1019
|
+
|
|
1020
|
+
### Live viewport
|
|
1021
|
+
|
|
1022
|
+
The web UI shows a real-time browser viewport panel alongside the chat when a browser session is active. Frames stream via CDP screencast through the \`/api/browser/stream\` SSE endpoint.
|
|
1023
|
+
|
|
1024
|
+
### Session persistence
|
|
1025
|
+
|
|
1026
|
+
Browser sessions are stored in profile directories (\`~/.poncho/browser-profiles/<agent-id>/\`). Cookies, localStorage, and other browser state persist across runs, so the agent can pick up where it left off (e.g., staying logged in across cron job runs).
|
|
1027
|
+
|
|
1028
|
+
### Stealth mode
|
|
1029
|
+
|
|
1030
|
+
Stealth mode is **enabled by default** (\`stealth: true\`) and reduces bot-detection fingerprints so websites treat the browser like a regular user session. It applies:
|
|
1031
|
+
|
|
1032
|
+
- A realistic Chrome user-agent string (matching the host OS)
|
|
1033
|
+
- \`--disable-blink-features=AutomationControlled\` flag
|
|
1034
|
+
- \`navigator.webdriver\` overridden to \`false\`
|
|
1035
|
+
- \`window.chrome\` shim for headless Chromium
|
|
1036
|
+
- Fake \`navigator.plugins\` (3 standard Chrome plugins)
|
|
1037
|
+
- \`navigator.languages\` fallback (\`['en-US', 'en']\`)
|
|
1038
|
+
- WebGL vendor/renderer patched to hide SwiftShader
|
|
1039
|
+
- \`Notification.permission\` patched for headless mode
|
|
1040
|
+
- Browser-level \`--user-agent\` flag (covers Web Workers)
|
|
1041
|
+
|
|
1042
|
+
To disable stealth mode (e.g., for trusted internal sites), set \`stealth: false\`. To use a custom user-agent while keeping other stealth patches, set \`userAgent\`:
|
|
1043
|
+
|
|
1044
|
+
\`\`\`javascript
|
|
1045
|
+
browser: {
|
|
1046
|
+
stealth: true, // default
|
|
1047
|
+
userAgent: "MyBot/1.0", // overrides the auto-detected Chrome UA
|
|
1048
|
+
}
|
|
1049
|
+
\`\`\`
|
|
1050
|
+
|
|
1051
|
+
### Setup
|
|
1052
|
+
|
|
1053
|
+
Install the browser package in your agent project:
|
|
1054
|
+
|
|
1055
|
+
\`\`\`bash
|
|
1056
|
+
pnpm add @poncho-ai/browser
|
|
1057
|
+
\`\`\`
|
|
1058
|
+
|
|
1059
|
+
Then set \`browser: true\` in \`poncho.config.js\`. Chromium is downloaded automatically via a \`postinstall\` hook (skipped when \`CI\` or \`SERVERLESS\` env vars are set).
|
|
1060
|
+
|
|
1061
|
+
**Serverless deployments** (Lambda, Vercel, etc.): use \`@sparticuz/chromium\` instead of the bundled Chromium:
|
|
1062
|
+
|
|
1063
|
+
\`\`\`bash
|
|
1064
|
+
pnpm add @sparticuz/chromium
|
|
1065
|
+
\`\`\`
|
|
1066
|
+
|
|
1067
|
+
\`\`\`javascript
|
|
1068
|
+
// poncho.config.js
|
|
1069
|
+
import chromium from "@sparticuz/chromium";
|
|
1070
|
+
|
|
1071
|
+
export default {
|
|
1072
|
+
browser: {
|
|
1073
|
+
executablePath: await chromium.executablePath(),
|
|
1074
|
+
headless: true,
|
|
1075
|
+
},
|
|
1076
|
+
};
|
|
1077
|
+
\`\`\`
|
|
1078
|
+
|
|
1079
|
+
## Subagents
|
|
1080
|
+
|
|
1081
|
+
Poncho agents can spawn recursive copies of themselves as **subagents**. Each subagent runs in its own independent conversation with full access to the agent's tools and skills. The parent agent controls the subagent lifecycle and receives results directly.
|
|
1082
|
+
|
|
1083
|
+
Subagents are useful when an agent needs to parallelize work, delegate a subtask, or isolate a line of investigation without polluting the main conversation context.
|
|
1084
|
+
|
|
1085
|
+
### How it works
|
|
1086
|
+
|
|
1087
|
+
When the agent decides to use a subagent, it calls \`spawn_subagent\` with a task description. The subagent runs to completion and the result is returned to the parent \u2014 the call is **blocking**, so the parent waits for the subagent to finish before continuing.
|
|
1088
|
+
|
|
1089
|
+
The parent can also send follow-up messages to existing subagents with \`message_subagent\`, stop a running subagent with \`stop_subagent\`, or list all its subagents with \`list_subagents\`.
|
|
1090
|
+
|
|
1091
|
+
### Available tools
|
|
1092
|
+
|
|
1093
|
+
| Tool | Description |
|
|
1094
|
+
|------|-------------|
|
|
1095
|
+
| \`spawn_subagent\` | Create a new subagent with a task. Blocks until the subagent completes and returns the result. |
|
|
1096
|
+
| \`message_subagent\` | Send a follow-up message to an existing subagent. Blocks until it responds. |
|
|
1097
|
+
| \`stop_subagent\` | Stop a running subagent. |
|
|
1098
|
+
| \`list_subagents\` | List all subagents for the current conversation with their IDs, tasks, and statuses. |
|
|
1099
|
+
|
|
1100
|
+
### Limits
|
|
1101
|
+
|
|
1102
|
+
- **Max depth**: 3 levels of nesting (an agent can spawn a subagent, which can spawn another, but no deeper).
|
|
1103
|
+
- **Max concurrent**: 5 subagents per parent conversation.
|
|
1104
|
+
|
|
1105
|
+
### Memory isolation
|
|
1106
|
+
|
|
1107
|
+
Subagents have **read-only** access to the parent agent's persistent memory. They can recall information but cannot modify the main memory document. This prevents subagents from accidentally overwriting each other's memory updates.
|
|
1108
|
+
|
|
1109
|
+
### Approvals
|
|
1110
|
+
|
|
1111
|
+
When a subagent invokes a tool that requires approval, the approval request is **tunneled to the parent conversation**. You'll see the approval prompt inline in the parent's message thread with a label indicating which subagent is asking. The parent conversation also shows an orange dot in the sidebar while any subagent is waiting for approval.
|
|
1112
|
+
|
|
1113
|
+
### Web UI
|
|
1114
|
+
|
|
1115
|
+
In the web UI, subagent conversations appear **nested under their parent** in the sidebar (tree-style indentation). Clicking a subagent conversation shows it in read-only mode \u2014 you can view the full context but cannot send messages, since the parent agent controls the subagent.
|
|
1116
|
+
|
|
1117
|
+
When the parent conversation is active, \`spawn_subagent\` tool calls in the tool activity timeline are clickable links that navigate to the subagent's conversation.
|
|
1118
|
+
|
|
1119
|
+
## Persistent Memory
|
|
1120
|
+
|
|
1121
|
+
When \`memory.enabled\` is true in \`poncho.config.js\`, the harness enables a simple memory model:
|
|
1122
|
+
|
|
1123
|
+
- A single persistent main memory document is loaded at run start and interpolated into the system prompt under \`## Persistent Memory\`.
|
|
1124
|
+
- \`memory_main_update\` can replace or append to that document. The tool description instructs the model to proactively evaluate each turn whether durable memory should be updated.
|
|
1125
|
+
- \`conversation_recall\` can search recent prior conversations (keyword scoring) when historical context is relevant (\`as we discussed\`, \`last time\`, etc.).
|
|
1126
|
+
|
|
1127
|
+
\`\`\`javascript
|
|
1128
|
+
// poncho.config.js
|
|
1129
|
+
export default {
|
|
1130
|
+
storage: {
|
|
1131
|
+
provider: 'local', // 'local' | 'memory' | 'redis' | 'upstash' | 'dynamodb'
|
|
1132
|
+
memory: {
|
|
1133
|
+
enabled: true,
|
|
1134
|
+
maxRecallConversations: 20, // Bounds conversation_recall scan size
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
}
|
|
1138
|
+
\`\`\`
|
|
1139
|
+
|
|
1140
|
+
Available memory tools:
|
|
1141
|
+
|
|
1142
|
+
- \`memory_main_get\`
|
|
1143
|
+
- \`memory_main_update\`
|
|
1144
|
+
- \`conversation_recall\`
|
|
1145
|
+
`,
|
|
1146
|
+
"configuration": `# Configuration & Security
|
|
1147
|
+
|
|
1148
|
+
> Back to [README](../README.md)
|
|
1149
|
+
|
|
1150
|
+
## Credential Pattern
|
|
1151
|
+
|
|
1152
|
+
All credentials in \`poncho.config.js\` use **env var name** fields (\`*Env\` suffix). The config specifies *which* environment variable to read \u2014 never the secret value itself. Every \`*Env\` field has a sensible default, so you only need to set the field when your env var name differs from the convention:
|
|
1153
|
+
|
|
1154
|
+
| Config field | Default env var | Purpose |
|
|
1155
|
+
|---|---|---|
|
|
1156
|
+
| \`providers.anthropic.apiKeyEnv\` | \`ANTHROPIC_API_KEY\` | Anthropic model API key |
|
|
1157
|
+
| \`providers.openai.apiKeyEnv\` | \`OPENAI_API_KEY\` | OpenAI model API key |
|
|
1158
|
+
| \`auth.tokenEnv\` | \`PONCHO_AUTH_TOKEN\` | Auth passphrase / bearer token |
|
|
1159
|
+
| \`storage.urlEnv\` | \`UPSTASH_REDIS_REST_URL\` / \`REDIS_URL\` | Storage connection URL |
|
|
1160
|
+
| \`storage.tokenEnv\` | \`UPSTASH_REDIS_REST_TOKEN\` | Upstash REST token |
|
|
1161
|
+
| \`telemetry.latitude.apiKeyEnv\` | \`LATITUDE_API_KEY\` | Latitude API key |
|
|
1162
|
+
| \`telemetry.latitude.projectIdEnv\` | \`LATITUDE_PROJECT_ID\` | Latitude project ID |
|
|
1163
|
+
| \`messaging[].botTokenEnv\` | \`SLACK_BOT_TOKEN\` | Slack bot token |
|
|
1164
|
+
| \`messaging[].signingSecretEnv\` | \`SLACK_SIGNING_SECRET\` | Slack signing secret |
|
|
1165
|
+
| \`messaging[].botTokenEnv\` | \`TELEGRAM_BOT_TOKEN\` | Telegram bot token |
|
|
1166
|
+
| \`messaging[].webhookSecretEnv\` | \`TELEGRAM_WEBHOOK_SECRET\` | Telegram webhook secret (optional) |
|
|
1167
|
+
| \`messaging[].apiKeyEnv\` | \`RESEND_API_KEY\` | Resend API key |
|
|
1168
|
+
| \`messaging[].webhookSecretEnv\` | \`RESEND_WEBHOOK_SECRET\` | Resend webhook signing secret |
|
|
1169
|
+
| \`messaging[].fromEnv\` | \`RESEND_FROM\` | Resend sender address |
|
|
1170
|
+
| \`messaging[].replyToEnv\` | \`RESEND_REPLY_TO\` | Resend reply-to address (optional) |
|
|
1171
|
+
| \`mcp[].auth.tokenEnv\` | *(user-defined)* | MCP server bearer token |
|
|
1172
|
+
|
|
1173
|
+
## Config File Reference (\`poncho.config.js\`)
|
|
1174
|
+
|
|
1175
|
+
\`\`\`javascript
|
|
1176
|
+
export default {
|
|
1177
|
+
// Custom harness (default: @poncho-ai/harness)
|
|
1178
|
+
harness: '@poncho-ai/harness', // or './my-harness.js'
|
|
1179
|
+
|
|
1180
|
+
// MCP servers (remote)
|
|
1181
|
+
mcp: [
|
|
1182
|
+
// Remote: Connect to external server
|
|
1183
|
+
{
|
|
1184
|
+
name: 'github',
|
|
1185
|
+
url: 'https://mcp.example.com/github',
|
|
1186
|
+
auth: { type: 'bearer', tokenEnv: 'GITHUB_TOKEN' },
|
|
1187
|
+
}
|
|
1188
|
+
],
|
|
1189
|
+
|
|
1190
|
+
// Extra directories to scan for skills (skills/ is always scanned)
|
|
1191
|
+
skillPaths: ['.cursor/skills'],
|
|
1192
|
+
|
|
1193
|
+
// Tool access: true (available), false (disabled), 'approval' (requires human approval)
|
|
1194
|
+
// Any tool name works \u2014 harness tools, adapter tools (send_email), MCP tools, etc.
|
|
1195
|
+
tools: {
|
|
1196
|
+
list_directory: true, // available (default)
|
|
1197
|
+
read_file: true, // available (default)
|
|
1198
|
+
write_file: true, // gated by environment for writes
|
|
1199
|
+
delete_file: 'approval', // requires human approval
|
|
1200
|
+
delete_directory: 'approval', // requires human approval
|
|
1201
|
+
send_email: 'approval', // requires human approval
|
|
1202
|
+
byEnvironment: {
|
|
1203
|
+
production: {
|
|
1204
|
+
write_file: false, // disable writes in production
|
|
1205
|
+
delete_file: false, // disable deletes in production
|
|
1206
|
+
delete_directory: false, // disable deletes in production
|
|
1207
|
+
send_email: 'approval', // keep approval in production
|
|
1208
|
+
},
|
|
1209
|
+
development: {
|
|
1210
|
+
send_email: true, // skip approval in dev
|
|
1211
|
+
},
|
|
1212
|
+
},
|
|
1213
|
+
},
|
|
1214
|
+
|
|
1215
|
+
// Authentication (protects both Web UI and API)
|
|
1216
|
+
auth: {
|
|
1217
|
+
required: true,
|
|
1218
|
+
type: 'bearer', // 'bearer' | 'header' | 'custom'
|
|
1219
|
+
// tokenEnv: 'PONCHO_AUTH_TOKEN', // env var name (default)
|
|
1220
|
+
},
|
|
1221
|
+
// When auth.required is true:
|
|
1222
|
+
// - Web UI: users enter the passphrase (value of PONCHO_AUTH_TOKEN env var)
|
|
1223
|
+
// - API: clients include Authorization: Bearer <token> header
|
|
1224
|
+
|
|
1225
|
+
// Model provider API key env var overrides (optional)
|
|
1226
|
+
providers: {
|
|
1227
|
+
// anthropic: { apiKeyEnv: 'ANTHROPIC_API_KEY' }, // default
|
|
1228
|
+
// openai: { apiKeyEnv: 'OPENAI_API_KEY' }, // default
|
|
1229
|
+
},
|
|
1230
|
+
|
|
1231
|
+
// Unified storage (preferred). Replaces separate \`state\` and \`memory\` blocks.
|
|
1232
|
+
// Credentials are read from env vars; override the var name with *Env fields.
|
|
1233
|
+
storage: {
|
|
1234
|
+
provider: 'upstash', // 'local' | 'memory' | 'redis' | 'upstash' | 'dynamodb'
|
|
1235
|
+
// urlEnv: 'UPSTASH_REDIS_REST_URL', // default (falls back to KV_REST_API_URL)
|
|
1236
|
+
// tokenEnv: 'UPSTASH_REDIS_REST_TOKEN', // default (falls back to KV_REST_API_TOKEN)
|
|
1237
|
+
ttl: {
|
|
1238
|
+
conversations: 3600, // seconds
|
|
1239
|
+
memory: 0, // 0/undefined means no expiration
|
|
1240
|
+
},
|
|
1241
|
+
memory: {
|
|
1242
|
+
enabled: true,
|
|
1243
|
+
maxRecallConversations: 20, // Bounds conversation_recall scan size
|
|
1244
|
+
},
|
|
1245
|
+
},
|
|
1246
|
+
|
|
1247
|
+
// Telemetry destination
|
|
1248
|
+
telemetry: {
|
|
1249
|
+
enabled: true,
|
|
1250
|
+
otlp: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
1251
|
+
// Or use Latitude (reads from LATITUDE_API_KEY and LATITUDE_PROJECT_ID env vars by default)
|
|
1252
|
+
latitude: {
|
|
1253
|
+
// apiKeyEnv: 'LATITUDE_API_KEY', // default
|
|
1254
|
+
// projectIdEnv: 'LATITUDE_PROJECT_ID', // default
|
|
1255
|
+
path: 'your/prompt-path', // optional, defaults to agent name
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
|
|
1259
|
+
// Messaging platform integrations
|
|
1260
|
+
messaging: [
|
|
1261
|
+
{ platform: 'slack' }, // Uses SLACK_BOT_TOKEN + SLACK_SIGNING_SECRET
|
|
1262
|
+
// { platform: 'slack', botTokenEnv: 'MY_BOT_TOKEN' }, // Custom env var names
|
|
1263
|
+
{ platform: 'telegram' }, // Uses TELEGRAM_BOT_TOKEN (+ optional TELEGRAM_WEBHOOK_SECRET)
|
|
1264
|
+
// { platform: 'telegram', botTokenEnv: 'MY_TG_TOKEN' }, // Custom env var names
|
|
1265
|
+
{ platform: 'resend' }, // Uses RESEND_API_KEY + RESEND_WEBHOOK_SECRET + RESEND_FROM
|
|
1266
|
+
// { platform: 'resend', mode: 'tool', replyToEnv: 'RESEND_REPLY_TO' }, // Tool mode with custom reply-to
|
|
1267
|
+
],
|
|
1268
|
+
|
|
1269
|
+
// File upload storage (default: local filesystem)
|
|
1270
|
+
uploads: {
|
|
1271
|
+
provider: 'local', // 'local' | 'vercel-blob' | 's3'
|
|
1272
|
+
// access: 'public', // vercel-blob access mode
|
|
1273
|
+
// bucket: 'my-uploads', // S3 bucket name
|
|
1274
|
+
// region: 'us-east-1', // S3 region
|
|
1275
|
+
// endpoint: '...', // S3-compatible endpoint
|
|
1276
|
+
},
|
|
1277
|
+
|
|
1278
|
+
// Browser automation (requires @poncho-ai/browser)
|
|
1279
|
+
// browser: true,
|
|
1280
|
+
// browser: {
|
|
1281
|
+
// viewport: { width: 1280, height: 720 },
|
|
1282
|
+
// quality: 80,
|
|
1283
|
+
// everyNthFrame: 2,
|
|
1284
|
+
// headless: true,
|
|
1285
|
+
// profileDir: '~/.poncho/browser-profiles',
|
|
1286
|
+
// executablePath: '/path/to/chromium',
|
|
1287
|
+
// stealth: true, // Anti-bot-detection (default: true)
|
|
1288
|
+
// userAgent: 'custom UA', // Override the default stealth user-agent
|
|
1289
|
+
// },
|
|
1290
|
+
|
|
1291
|
+
// Headless mode: disable the built-in Web UI (API-only)
|
|
1292
|
+
// webUi: false,
|
|
1293
|
+
|
|
1294
|
+
}
|
|
1295
|
+
\`\`\`
|
|
1296
|
+
|
|
1297
|
+
\`provider: 'local'\` stores runtime state under \`~/.poncho/store\` (or \`/tmp/.poncho/store\` on serverless runtimes), scoped by both agent name and stable agent id:
|
|
1298
|
+
|
|
1299
|
+
\`\`\`text
|
|
1300
|
+
~/.poncho/store
|
|
1301
|
+
\u2514\u2500\u2500 my-agent--agent_01f4f5d7e9c7432da51f8c6b9e2b1a0c
|
|
1302
|
+
\u251C\u2500\u2500 memory.json
|
|
1303
|
+
\u251C\u2500\u2500 state.json
|
|
1304
|
+
\u251C\u2500\u2500 onboarding-state.json
|
|
1305
|
+
\u2514\u2500\u2500 conversations
|
|
1306
|
+
\u251C\u2500\u2500 index.json
|
|
1307
|
+
\u251C\u2500\u2500 20260217T154233Z--conv_01j9x8a12bcd.json
|
|
1308
|
+
\u2514\u2500\u2500 20260218T101004Z--conv_01j9x8b45efg.json
|
|
1309
|
+
\`\`\`
|
|
1310
|
+
|
|
1311
|
+
Remote storage keys are namespaced and versioned, for example \`poncho:v1:<agentId>:...\`.
|
|
1312
|
+
|
|
1313
|
+
## Environment Variables
|
|
1314
|
+
|
|
1315
|
+
| Variable | Required | Description |
|
|
1316
|
+
|----------|----------|-------------|
|
|
1317
|
+
| \`ANTHROPIC_API_KEY\` | Yes* | Claude API key |
|
|
1318
|
+
| \`OPENAI_API_KEY\` | No | OpenAI API key (if using OpenAI) |
|
|
1319
|
+
| \`PONCHO_AUTH_TOKEN\` | No | Unified auth token (Web UI passphrase + API Bearer token) |
|
|
1320
|
+
| \`OTEL_EXPORTER_OTLP_ENDPOINT\` | No | Telemetry destination |
|
|
1321
|
+
| \`LATITUDE_API_KEY\` | No | Latitude dashboard integration |
|
|
1322
|
+
| \`LATITUDE_PROJECT_ID\` | No | Latitude project identifier for capture traces |
|
|
1323
|
+
| \`LATITUDE_PATH\` | No | Latitude prompt path for grouping traces |
|
|
1324
|
+
| \`KV_REST_API_URL\` | No | Upstash REST URL (Vercel Marketplace naming) |
|
|
1325
|
+
| \`KV_REST_API_TOKEN\` | No | Upstash REST write token (Vercel Marketplace naming) |
|
|
1326
|
+
| \`UPSTASH_REDIS_REST_URL\` | No | Upstash REST URL (direct Upstash naming) |
|
|
1327
|
+
| \`UPSTASH_REDIS_REST_TOKEN\` | No | Upstash REST write token (direct Upstash naming) |
|
|
1328
|
+
| \`REDIS_URL\` | No | For Redis state storage |
|
|
1329
|
+
| \`SLACK_BOT_TOKEN\` | No | Slack Bot Token (for messaging integration) |
|
|
1330
|
+
| \`SLACK_SIGNING_SECRET\` | No | Slack Signing Secret (for messaging integration) |
|
|
1331
|
+
| \`TELEGRAM_BOT_TOKEN\` | No | Telegram Bot Token (from @BotFather) |
|
|
1332
|
+
| \`TELEGRAM_WEBHOOK_SECRET\` | No | Telegram webhook secret token (optional) |
|
|
1333
|
+
| \`RESEND_API_KEY\` | No | Resend API key (for email messaging) |
|
|
1334
|
+
| \`RESEND_WEBHOOK_SECRET\` | No | Resend webhook signing secret |
|
|
1335
|
+
| \`RESEND_FROM\` | No | Sender address for email replies |
|
|
1336
|
+
| \`RESEND_REPLY_TO\` | No | Reply-to address for outgoing emails (optional) |
|
|
1337
|
+
| \`BLOB_READ_WRITE_TOKEN\` | No | Vercel Blob token (for \`uploads.provider: 'vercel-blob'\`) |
|
|
1338
|
+
| \`PONCHO_UPLOADS_BUCKET\` | No | S3 bucket name (for \`uploads.provider: 's3'\`) |
|
|
1339
|
+
|
|
1340
|
+
*Required if using Anthropic models (default).
|
|
1341
|
+
|
|
1342
|
+
## Observability
|
|
1343
|
+
|
|
1344
|
+
### Local development
|
|
1345
|
+
|
|
1346
|
+
Logs print to console:
|
|
1347
|
+
|
|
1348
|
+
\`\`\`
|
|
1349
|
+
[event] run:started {"type":"run:started","runId":"run_abc123","agentId":"my-agent"}
|
|
1350
|
+
[event] tool:started {"type":"tool:started","tool":"read_file","input":{"path":"README.md"}}
|
|
1351
|
+
[event] tool:completed {"type":"tool:completed","tool":"read_file","duration":45,"output":{"path":"README.md","content":"..."}}
|
|
1352
|
+
[event] run:completed {"type":"run:completed","runId":"run_abc123","result":{"status":"completed","response":"...","steps":3,"tokens":{"input":1500,"output":840}}}
|
|
1353
|
+
\`\`\`
|
|
1354
|
+
|
|
1355
|
+
### Production telemetry
|
|
1356
|
+
|
|
1357
|
+
Send events to your observability stack:
|
|
1358
|
+
|
|
1359
|
+
\`\`\`bash
|
|
1360
|
+
# Environment variable
|
|
1361
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.com
|
|
1362
|
+
\`\`\`
|
|
1363
|
+
|
|
1364
|
+
Or configure in code:
|
|
1365
|
+
|
|
1366
|
+
\`\`\`javascript
|
|
1367
|
+
// poncho.config.js
|
|
1368
|
+
export default {
|
|
1369
|
+
telemetry: {
|
|
1370
|
+
otlp: 'https://otel.example.com',
|
|
1371
|
+
// Or custom handler
|
|
1372
|
+
handler: async (event) => {
|
|
1373
|
+
await sendToMyLoggingService(event)
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
\`\`\`
|
|
1378
|
+
|
|
1379
|
+
### Latitude integration (optional)
|
|
1380
|
+
|
|
1381
|
+
Send traces to [Latitude](https://latitude.so) for a dashboard with cost tracking and prompt management:
|
|
1382
|
+
|
|
1383
|
+
\`\`\`bash
|
|
1384
|
+
LATITUDE_API_KEY=lat_xxx
|
|
1385
|
+
LATITUDE_PROJECT_ID=123
|
|
1386
|
+
LATITUDE_PATH=agents/my-agent/run
|
|
1387
|
+
\`\`\`
|
|
1388
|
+
|
|
1389
|
+
Or configure via \`poncho.config.js\`:
|
|
1390
|
+
|
|
1391
|
+
\`\`\`javascript
|
|
1392
|
+
telemetry: {
|
|
1393
|
+
latitude: {
|
|
1394
|
+
// apiKeyEnv: 'LATITUDE_API_KEY', // default
|
|
1395
|
+
// projectIdEnv: 'LATITUDE_PROJECT_ID', // default
|
|
1396
|
+
path: 'your/prompt-path',
|
|
1397
|
+
},
|
|
1398
|
+
}
|
|
1399
|
+
\`\`\`
|
|
1400
|
+
|
|
1401
|
+
## Security
|
|
1402
|
+
|
|
1403
|
+
### Protect your endpoint
|
|
1404
|
+
|
|
1405
|
+
Enable authentication to secure both the Web UI and API:
|
|
1406
|
+
|
|
1407
|
+
\`\`\`javascript
|
|
1408
|
+
// poncho.config.js
|
|
1409
|
+
export default {
|
|
1410
|
+
auth: {
|
|
1411
|
+
required: true,
|
|
1412
|
+
type: 'bearer' // Default: validates against PONCHO_AUTH_TOKEN
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
\`\`\`
|
|
1416
|
+
|
|
1417
|
+
\`\`\`bash
|
|
1418
|
+
# .env
|
|
1419
|
+
PONCHO_AUTH_TOKEN=your-secret-token-here
|
|
1420
|
+
\`\`\`
|
|
1421
|
+
|
|
1422
|
+
With \`auth.required: true\`:
|
|
1423
|
+
- **Web UI**: Users must enter \`PONCHO_AUTH_TOKEN\` as the passphrase to login
|
|
1424
|
+
- **API**: Clients must include \`Authorization: Bearer <PONCHO_AUTH_TOKEN>\` header
|
|
1425
|
+
|
|
1426
|
+
For custom validation:
|
|
1427
|
+
|
|
1428
|
+
\`\`\`javascript
|
|
1429
|
+
// poncho.config.js
|
|
1430
|
+
export default {
|
|
1431
|
+
auth: {
|
|
1432
|
+
required: true,
|
|
1433
|
+
type: 'custom',
|
|
1434
|
+
validate: async (token) => {
|
|
1435
|
+
// Custom logic: check database, verify JWT, etc.
|
|
1436
|
+
return token === process.env.PONCHO_AUTH_TOKEN
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
\`\`\`
|
|
1441
|
+
|
|
1442
|
+
### Require approval for dangerous tools
|
|
1443
|
+
|
|
1444
|
+
Use \`approval-required\` in your \`AGENT.md\` or \`SKILL.md\` frontmatter to gate specific tools:
|
|
1445
|
+
|
|
1446
|
+
\`\`\`yaml
|
|
1447
|
+
---
|
|
1448
|
+
allowed-tools:
|
|
1449
|
+
- mcp:linear/*
|
|
1450
|
+
approval-required:
|
|
1451
|
+
- mcp:linear/list_initiatives
|
|
1452
|
+
---
|
|
1453
|
+
\`\`\`
|
|
1454
|
+
|
|
1455
|
+
When a gated tool is called, the harness emits a \`tool:approval:required\` event and pauses until approval is granted or denied. In the web UI and interactive CLI, the user is prompted before execution continues.
|
|
1456
|
+
`,
|
|
1457
|
+
"troubleshooting": `# Error Handling & Troubleshooting
|
|
1458
|
+
|
|
1459
|
+
> Back to [README](../README.md)
|
|
1460
|
+
|
|
1461
|
+
## Error Types
|
|
1462
|
+
|
|
1463
|
+
These error codes appear in \`run:error\` SSE events:
|
|
1464
|
+
|
|
1465
|
+
| Code | Description |
|
|
1466
|
+
|------|-------------|
|
|
1467
|
+
| \`MAX_STEPS_EXCEEDED\` | Agent hit the step limit without completing |
|
|
1468
|
+
| \`TIMEOUT\` | Agent exceeded the timeout |
|
|
1469
|
+
| \`MODEL_TIMEOUT\` | Model API call timed out |
|
|
1470
|
+
| \`MODEL_ERROR\` | Model API returned an error |
|
|
1471
|
+
| \`CONTENT_FILTER\` | Response blocked by the provider's content filter |
|
|
1472
|
+
| \`AUTH_ERROR\` | Authentication failed |
|
|
1473
|
+
|
|
1474
|
+
## Tool Errors Are Recoverable
|
|
1475
|
+
|
|
1476
|
+
When a tool fails, the error is sent back to the model via a \`tool:error\` event. The model can retry with different parameters, try a different tool, or ask the user for help:
|
|
1477
|
+
|
|
1478
|
+
\`\`\`
|
|
1479
|
+
event: tool:error
|
|
1480
|
+
data: {"tool": "fetch-url", "error": "Connection timeout", "recoverable": true}
|
|
1481
|
+
|
|
1482
|
+
event: model:chunk
|
|
1483
|
+
data: {"content": "I couldn't fetch that URL. Let me try a different approach..."}
|
|
1484
|
+
\`\`\`
|
|
1485
|
+
|
|
1486
|
+
## Fatal Errors End the Run
|
|
1487
|
+
|
|
1488
|
+
Timeout, max steps, or model API errors end the run immediately:
|
|
1489
|
+
|
|
1490
|
+
\`\`\`
|
|
1491
|
+
event: run:error
|
|
1492
|
+
data: {"runId": "run_abc", "error": {"code": "TIMEOUT", "message": "Run exceeded 60 second timeout"}}
|
|
1493
|
+
\`\`\`
|
|
1494
|
+
|
|
1495
|
+
## Handle Errors in Your Client
|
|
1496
|
+
|
|
1497
|
+
\`\`\`typescript
|
|
1498
|
+
const agent = new AgentClient({ url: 'https://my-agent.vercel.app' })
|
|
1499
|
+
|
|
1500
|
+
try {
|
|
1501
|
+
const result = await agent.run({ task: 'Do something' })
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
console.error('Agent run failed:', error.message)
|
|
1504
|
+
}
|
|
1505
|
+
\`\`\`
|
|
1506
|
+
|
|
1507
|
+
## Troubleshooting
|
|
1508
|
+
|
|
1509
|
+
### "ANTHROPIC_API_KEY not set"
|
|
1510
|
+
|
|
1511
|
+
Make sure you have a \`.env\` file with your API key:
|
|
1512
|
+
|
|
1513
|
+
\`\`\`bash
|
|
1514
|
+
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
|
|
1515
|
+
\`\`\`
|
|
1516
|
+
|
|
1517
|
+
### "MCP server failed to connect"
|
|
1518
|
+
|
|
1519
|
+
Check that:
|
|
1520
|
+
1. A remote MCP server is configured (\`poncho mcp list\`)
|
|
1521
|
+
2. The MCP URL is correct and reachable (\`http://\` or \`https://\`)
|
|
1522
|
+
3. Required environment variables/secrets are set
|
|
1523
|
+
4. Any required auth headers/tokens expected by the remote server are configured
|
|
1524
|
+
|
|
1525
|
+
### Agent keeps running forever
|
|
1526
|
+
|
|
1527
|
+
Set execution limits:
|
|
1528
|
+
|
|
1529
|
+
\`\`\`yaml
|
|
1530
|
+
---
|
|
1531
|
+
limits:
|
|
1532
|
+
maxSteps: 20
|
|
1533
|
+
timeout: 60 # 1 minute (in seconds)
|
|
1534
|
+
---
|
|
1535
|
+
\`\`\`
|
|
1536
|
+
|
|
1537
|
+
### Vercel deploy issues
|
|
1538
|
+
|
|
1539
|
+
- After upgrading \`@poncho-ai/cli\`, re-run \`poncho build vercel --force\` to refresh generated deploy files.
|
|
1540
|
+
- If Vercel fails during \`pnpm install\` due to a lockfile mismatch, run \`pnpm install --no-frozen-lockfile\` locally and commit \`pnpm-lock.yaml\`.
|
|
1541
|
+
- Deploy from the project root: \`vercel deploy --prod\`.
|
|
1542
|
+
`
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
// src/default-tools.ts
|
|
409
1546
|
var resolveSafePath = (workingDir, inputPath) => {
|
|
410
1547
|
const base = resolve4(workingDir);
|
|
411
1548
|
const target = resolve4(base, inputPath);
|
|
@@ -534,6 +1671,31 @@ var createDeleteDirectoryTool = (workingDir) => defineTool({
|
|
|
534
1671
|
return { path, deleted: true };
|
|
535
1672
|
}
|
|
536
1673
|
});
|
|
1674
|
+
var PONCHO_DOCS_TOPICS = Object.keys(PONCHO_DOCS);
|
|
1675
|
+
var ponchoDocsTool = defineTool({
|
|
1676
|
+
name: "poncho_docs",
|
|
1677
|
+
description: `Read detailed Poncho framework documentation by topic. Available topics: ${PONCHO_DOCS_TOPICS.join(", ")}.`,
|
|
1678
|
+
inputSchema: {
|
|
1679
|
+
type: "object",
|
|
1680
|
+
properties: {
|
|
1681
|
+
topic: {
|
|
1682
|
+
type: "string",
|
|
1683
|
+
enum: PONCHO_DOCS_TOPICS,
|
|
1684
|
+
description: "Documentation topic to read"
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
required: ["topic"],
|
|
1688
|
+
additionalProperties: false
|
|
1689
|
+
},
|
|
1690
|
+
handler: async (input) => {
|
|
1691
|
+
const topic = typeof input.topic === "string" ? input.topic : "";
|
|
1692
|
+
const content = PONCHO_DOCS[topic];
|
|
1693
|
+
if (!content) {
|
|
1694
|
+
return { error: `Unknown topic "${topic}". Available: ${PONCHO_DOCS_TOPICS.join(", ")}` };
|
|
1695
|
+
}
|
|
1696
|
+
return { topic, content };
|
|
1697
|
+
}
|
|
1698
|
+
});
|
|
537
1699
|
|
|
538
1700
|
// src/harness.ts
|
|
539
1701
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
@@ -3141,7 +4303,15 @@ Since all fields have defaults, you only need to specify \`*Env\` when your env
|
|
|
3141
4303
|
- If shell/CLI access is unavailable, ask the user to run needed commands and provide exact copy-paste commands.
|
|
3142
4304
|
- For setup, skills, MCP, auth, storage, telemetry, or "how do I..." questions, proactively read \`README.md\` with \`read_file\` before answering.
|
|
3143
4305
|
- Prefer quoting concrete commands and examples from \`README.md\` over guessing.
|
|
3144
|
-
- Keep edits minimal, preserve unrelated settings/code, and summarize what changed
|
|
4306
|
+
- Keep edits minimal, preserve unrelated settings/code, and summarize what changed.
|
|
4307
|
+
|
|
4308
|
+
## Detailed Documentation
|
|
4309
|
+
|
|
4310
|
+
For topics not covered above, use the \`poncho_docs\` tool to load full documentation on demand:
|
|
4311
|
+
- \`api\` \u2014 HTTP API endpoints, SSE events, TypeScript client SDK, file attachments, upload providers
|
|
4312
|
+
- \`features\` \u2014 Web UI details, browser automation, subagents, persistent memory, custom messaging adapters
|
|
4313
|
+
- \`configuration\` \u2014 Full config reference, env vars, auth types, storage, telemetry, tool approval
|
|
4314
|
+
- \`troubleshooting\` \u2014 Error codes, recoverable vs fatal errors, common issues and fixes`;
|
|
3145
4315
|
function extractMediaFromToolOutput(output) {
|
|
3146
4316
|
const mediaItems = [];
|
|
3147
4317
|
function walk(node) {
|
|
@@ -3251,6 +4421,9 @@ var AgentHarness = class {
|
|
|
3251
4421
|
if (this.isToolEnabled("delete_directory")) {
|
|
3252
4422
|
this.registerIfMissing(createDeleteDirectoryTool(this.workingDir));
|
|
3253
4423
|
}
|
|
4424
|
+
if (this.environment === "development" && this.isToolEnabled("poncho_docs")) {
|
|
4425
|
+
this.registerIfMissing(ponchoDocsTool);
|
|
4426
|
+
}
|
|
3254
4427
|
}
|
|
3255
4428
|
shouldEnableWriteTool() {
|
|
3256
4429
|
const override = process.env.PONCHO_FS_WRITE?.toLowerCase();
|
|
@@ -5867,6 +7040,7 @@ export {
|
|
|
5867
7040
|
normalizeScriptPolicyPath,
|
|
5868
7041
|
parseAgentFile,
|
|
5869
7042
|
parseAgentMarkdown,
|
|
7043
|
+
ponchoDocsTool,
|
|
5870
7044
|
readSkillResource,
|
|
5871
7045
|
renderAgentPrompt,
|
|
5872
7046
|
resolveAgentIdentity,
|