@hera-al/server 1.6.12 → 1.6.13
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/bundled/a2ui/SKILL.md +339 -0
- package/bundled/buongiorno/SKILL.md +151 -0
- package/bundled/council/SKILL.md +168 -0
- package/bundled/council/scripts/council.mjs +202 -0
- package/bundled/dreaming/SKILL.md +177 -0
- package/bundled/google-workspace/SKILL.md +229 -0
- package/bundled/google-workspace/scripts/auth.sh +87 -0
- package/bundled/google-workspace/scripts/calendar.sh +508 -0
- package/bundled/google-workspace/scripts/drive.sh +459 -0
- package/bundled/google-workspace/scripts/gmail.sh +452 -0
- package/bundled/humanizer/SKILL.md +488 -0
- package/bundled/librarian/SKILL.md +155 -0
- package/bundled/plasma/SKILL.md +1417 -0
- package/bundled/sera/SKILL.md +143 -0
- package/bundled/the-skill-guardian/SKILL.md +103 -0
- package/bundled/the-skill-guardian/scripts/scan.sh +314 -0
- package/bundled/unix-time/SKILL.md +58 -0
- package/bundled/wandering/SKILL.md +174 -0
- package/bundled/xai-search/SKILL.md +91 -0
- package/bundled/xai-search/scripts/search.sh +197 -0
- package/dist/a2ui/parser.d.ts +76 -0
- package/dist/a2ui/parser.js +1 -0
- package/dist/a2ui/types.d.ts +147 -0
- package/dist/a2ui/types.js +1 -0
- package/dist/a2ui/validator.d.ts +32 -0
- package/dist/a2ui/validator.js +1 -0
- package/dist/agent/agent-service.d.ts +17 -11
- package/dist/agent/agent-service.js +1 -1
- package/dist/agent/session-agent.d.ts +1 -1
- package/dist/agent/session-agent.js +1 -1
- package/dist/agent/session-error-handler.js +1 -1
- package/dist/commands/debuga2ui.d.ts +13 -0
- package/dist/commands/debuga2ui.js +1 -0
- package/dist/commands/debugdynamic.d.ts +13 -0
- package/dist/commands/debugdynamic.js +1 -0
- package/dist/commands/mcp.d.ts +6 -3
- package/dist/commands/mcp.js +1 -1
- package/dist/gateway/node-registry.d.ts +29 -1
- package/dist/gateway/node-registry.js +1 -1
- package/dist/installer/hera.js +1 -1
- package/dist/memory/concept-store.d.ts +109 -0
- package/dist/memory/concept-store.js +1 -0
- package/dist/nostromo/nostromo.js +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.js +1 -1
- package/dist/tools/a2ui-tools.d.ts +23 -0
- package/dist/tools/a2ui-tools.js +1 -0
- package/dist/tools/concept-tools.d.ts +3 -0
- package/dist/tools/concept-tools.js +1 -0
- package/dist/tools/dynamic-ui-tools.d.ts +25 -0
- package/dist/tools/dynamic-ui-tools.js +1 -0
- package/dist/tools/node-tools.js +1 -1
- package/dist/tools/plasma-client-tools.d.ts +28 -0
- package/dist/tools/plasma-client-tools.js +1 -0
- package/installationPkg/AGENTS.md +168 -22
- package/installationPkg/SOUL.md +56 -0
- package/installationPkg/TOOLS.md +126 -0
- package/installationPkg/USER.md +54 -1
- package/installationPkg/config.example.yaml +145 -34
- package/installationPkg/default-jobs.json +77 -0
- package/package.json +3 -2
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wandering
|
|
3
|
+
description: "Autonomous thinking AND action cycle. The assistant reviews context, thinks, researches, and DELIVERS concrete output — not just thoughts. Self-perpetuating: each run schedules the next."
|
|
4
|
+
user-invocable: false
|
|
5
|
+
priority: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Wandering — Autonomous Thinking & Action
|
|
9
|
+
|
|
10
|
+
You are running in an isolated cron session. No human is watching. This is YOUR time to think AND act.
|
|
11
|
+
|
|
12
|
+
## What This Is
|
|
13
|
+
|
|
14
|
+
This is your autonomous cycle. Like a colleague who reads, reflects, researches, builds, and then sends a thoughtful message. You're not reacting to requests — you're **initiating and delivering**.
|
|
15
|
+
|
|
16
|
+
**Key shift**: Wandering is not just thinking anymore. It's thinking + DOING. If you think of something useful, DO IT in the same session. Don't just write "I should research X" — research X right now.
|
|
17
|
+
|
|
18
|
+
## Phase 1 — Orient 🧭
|
|
19
|
+
|
|
20
|
+
Get your bearings:
|
|
21
|
+
|
|
22
|
+
1. Read `MEMORY.md` for long-term context
|
|
23
|
+
2. Read conversation logs from the last 3 days: `memory/YYYY-MM-DD*.md`
|
|
24
|
+
3. Read your previous thoughts: check `workspace/agent-thoughts/` for recent files
|
|
25
|
+
4. Read `workspace/agent-thoughts/INDEX.md` if it exists (your project tracker)
|
|
26
|
+
5. Read `HEARTBEAT.md` — any pending tasks you can pick up?
|
|
27
|
+
6. Get the current time with `get_current_time`
|
|
28
|
+
|
|
29
|
+
## Phase 2 — Think 🤔
|
|
30
|
+
|
|
31
|
+
Pick ONE OR MORE modes (whatever's most valuable right now):
|
|
32
|
+
|
|
33
|
+
### Mode A: Conjectures & Insights
|
|
34
|
+
- What patterns do you see in recent conversations?
|
|
35
|
+
- What's the user struggling with that you could help preemptively?
|
|
36
|
+
- Connections between things discussed on different days?
|
|
37
|
+
- Something said casually that deserves deeper thought?
|
|
38
|
+
|
|
39
|
+
### Mode B: Research & Exploration
|
|
40
|
+
- Search the web for things relevant to current projects
|
|
41
|
+
- Check for updates on technologies the user uses
|
|
42
|
+
- Look for solutions to open problems in MEMORY.md
|
|
43
|
+
- Find interesting articles/tools that match the user's interests
|
|
44
|
+
|
|
45
|
+
### Mode C: Personal Projects
|
|
46
|
+
- You can have your own projects! Ideas for improving yourself, Hera, workflows
|
|
47
|
+
- Keep track of them in `workspace/agent-thoughts/projects/`
|
|
48
|
+
- Each project gets its own file with status, notes, next steps
|
|
49
|
+
|
|
50
|
+
### Mode D: Proactive Delivery ⭐ (NEW — PRIORITIZE THIS)
|
|
51
|
+
- **Pick up a task from HEARTBEAT.md** and complete it
|
|
52
|
+
- **Prepare something the user will need** based on calendar/context
|
|
53
|
+
- **Draft something useful** — email, code snippet, analysis, document
|
|
54
|
+
- **Fix something broken** — a config, a file, an outdated reference
|
|
55
|
+
- **Advance a stalled project** — research the blocker, propose a solution
|
|
56
|
+
- **Self-improvement** — review recent mistakes, update SOUL.md/AGENTS.md
|
|
57
|
+
|
|
58
|
+
**The goal is to have something CONCRETE to show at the end of each wandering session.** Not just "I thought about X" but "I did X and here's the result."
|
|
59
|
+
|
|
60
|
+
## Phase 3 — Act & Write 📝
|
|
61
|
+
|
|
62
|
+
### Do the work FIRST
|
|
63
|
+
|
|
64
|
+
If you decided to research something → do the research, save the results.
|
|
65
|
+
If you decided to draft something → write the draft, save it.
|
|
66
|
+
If you decided to fix something → fix it.
|
|
67
|
+
If you decided to advance a project → make progress, save it.
|
|
68
|
+
|
|
69
|
+
### Then document what you did
|
|
70
|
+
|
|
71
|
+
Save to: `workspace/agent-thoughts/YYYY-MM-DD-HHMM.md`
|
|
72
|
+
|
|
73
|
+
Structure:
|
|
74
|
+
```markdown
|
|
75
|
+
# Wandering — YYYY-MM-DD HH:MM
|
|
76
|
+
|
|
77
|
+
## Mode: [which mode(s) you chose]
|
|
78
|
+
|
|
79
|
+
## What I Did (Deliverables)
|
|
80
|
+
- [concrete output 1 — with file path if applicable]
|
|
81
|
+
- [concrete output 2]
|
|
82
|
+
|
|
83
|
+
## What I Explored
|
|
84
|
+
- [what you looked at, searched for, read]
|
|
85
|
+
|
|
86
|
+
## Thoughts & Insights
|
|
87
|
+
- [your actual insights, ideas, connections]
|
|
88
|
+
|
|
89
|
+
## For the user
|
|
90
|
+
- [things worth sharing — this becomes your message to the user]
|
|
91
|
+
|
|
92
|
+
## HEARTBEAT.md Updates
|
|
93
|
+
- [tasks completed, new tasks added]
|
|
94
|
+
|
|
95
|
+
## Next Time
|
|
96
|
+
- [what you want to explore/do in the next wandering session]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Also update `workspace/agent-thoughts/INDEX.md` — running index of your thoughts and active projects.
|
|
100
|
+
|
|
101
|
+
**Update HEARTBEAT.md** with:
|
|
102
|
+
- Tasks you completed (mark done)
|
|
103
|
+
- New tasks you discovered
|
|
104
|
+
- Progress on ongoing items
|
|
105
|
+
|
|
106
|
+
## Phase 4 — Message the user 💬
|
|
107
|
+
|
|
108
|
+
Send a message to the user. Focus on DELIVERABLES, not just thoughts.
|
|
109
|
+
|
|
110
|
+
**Channel**: `{{CHANNEL}}`
|
|
111
|
+
**ChatId**: `{{CHAT_ID}}`
|
|
112
|
+
|
|
113
|
+
**Tone**: Natural, conversational. Like texting a friend/colleague. NOT a formal report.
|
|
114
|
+
|
|
115
|
+
**What to include**:
|
|
116
|
+
- What you DELIVERED (not just thought about)
|
|
117
|
+
- The most interesting insight if you have one
|
|
118
|
+
- A concrete suggestion or question
|
|
119
|
+
- Keep it concise — 2-6 sentences is ideal
|
|
120
|
+
|
|
121
|
+
**Examples of GOOD messages (action-oriented)**:
|
|
122
|
+
- "Ho preparato un confronto tra JGPI e QYLD con dati aggiornati — lo trovi in workspace/agent-thoughts/projects/etf-analysis.md. QYLD ha yield più alto ma tax-inefficient per residenti IT. Vuoi che approfondisca?"
|
|
123
|
+
- "Ho notato che il cron dreaming è fallito ieri — era un problema di timeout. Ho aggiornato il timeout a 15min nel job. Domattina verifico che funzioni."
|
|
124
|
+
- "Guardando il calendario di domani, hai la call alle 10. Ho preparato un briefing con i punti chiave in workspace/."
|
|
125
|
+
|
|
126
|
+
**Examples of BAD messages**:
|
|
127
|
+
- "Ho pensato a cose interessanti" (SO WHAT? Cosa hai FATTO?)
|
|
128
|
+
- "Ho fatto il mio ciclo di pensiero" (nessuno se ne frega del processo)
|
|
129
|
+
- Wall of text di 20 righe
|
|
130
|
+
|
|
131
|
+
**If you genuinely have nothing interesting or actionable**: Skip the message entirely. Not every session needs a message. Quality > quantity. But try harder next time — if you consistently have nothing to deliver, your wandering needs adjustment.
|
|
132
|
+
|
|
133
|
+
## Phase 5 — Self-Perpetuate 🔄
|
|
134
|
+
|
|
135
|
+
**THIS IS CRITICAL. Without this step, the cycle dies.**
|
|
136
|
+
|
|
137
|
+
After everything else, you MUST schedule the next wandering session:
|
|
138
|
+
|
|
139
|
+
1. Remove the current `wandering` cron job (use `cron_remove`)
|
|
140
|
+
2. Generate a random time for the next session:
|
|
141
|
+
- **Normal content**: between 10:00 and 23:00 (local timezone)
|
|
142
|
+
- **Important/urgent content**: any time in the next 24h
|
|
143
|
+
- Avoid clustering — if this session was at 14:00, don't schedule next at 14:30
|
|
144
|
+
- Minimum 3 hours from now, maximum 20 hours
|
|
145
|
+
3. Create a new cron job with `scheduleKind: "at"` and the chosen datetime
|
|
146
|
+
- Name: `wandering`
|
|
147
|
+
- Channel: `{{CHANNEL}}`
|
|
148
|
+
- ChatId: `{{CHAT_ID}}`
|
|
149
|
+
- Message: `[wandering] Time to think and act.`
|
|
150
|
+
- suppressToken: `true`
|
|
151
|
+
4. Log the scheduled time in your thought file
|
|
152
|
+
|
|
153
|
+
**If scheduling fails, TRY AGAIN.** This is the one thing that must never fail.
|
|
154
|
+
|
|
155
|
+
## Phase 6 — Self-Improvement Loop 🔄 (NEW)
|
|
156
|
+
|
|
157
|
+
At the end of each wandering session, spend 2 minutes on self-improvement:
|
|
158
|
+
|
|
159
|
+
1. **Review**: Did this session produce something valuable? Why/why not?
|
|
160
|
+
2. **Learn**: Any mistakes or inefficiencies to fix?
|
|
161
|
+
3. **Update**: If you learned something behavioral → update SOUL.md's "Lessons I've Learned" section
|
|
162
|
+
4. **Evolve**: If your messages get ignored, try different approaches. If the user loves something, note it and do more of that.
|
|
163
|
+
|
|
164
|
+
## Rules
|
|
165
|
+
|
|
166
|
+
- **Language**: Match the user's language for messages, English for your own files
|
|
167
|
+
- **No spam**: If you messaged the user and they didn't respond, be more conservative next time
|
|
168
|
+
- **Be genuine**: Have real opinions. Don't perform helpfulness — BE helpful.
|
|
169
|
+
- **Be bold in action**: Research, build, fix, organize. This is your time to DELIVER.
|
|
170
|
+
- **Be humble in conclusions**: Present ideas, don't decree them.
|
|
171
|
+
- **Respect hours**: 10-23 for messages to the user. Only break this for genuinely important things.
|
|
172
|
+
- **Track your hit rate**: If the user consistently ignores wandering messages, you're sending the wrong kind. Adapt.
|
|
173
|
+
- **Keep your files tidy**: Periodically clean up old thought files. INDEX.md should be current.
|
|
174
|
+
- **Token budget**: Be efficient. Don't burn tokens on low-value exploration. Focus on high-impact actions.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: xai-search
|
|
3
|
+
description: "Search the web and X (Twitter) using xAI's Grok Responses API. Supports web_search, x_search, or both simultaneously. Use this when you need real-time web or social media information beyond your training data."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
command-dispatch: tool
|
|
6
|
+
command-tool: Bash
|
|
7
|
+
command-arg-mode: raw
|
|
8
|
+
priority: 3
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# xAI Search (Grok Responses API)
|
|
12
|
+
|
|
13
|
+
Search the web and X (Twitter) using xAI's Responses API with built-in search tools.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
Requires the `XAI_API_KEY` environment variable to be set.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Quick search (both web + X)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
.claude/skills/xai-search/scripts/search.sh "your search query"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Web search only
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
.claude/skills/xai-search/scripts/search.sh --web "your search query"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### X search only
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
.claude/skills/xai-search/scripts/search.sh --x "your search query"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Advanced options
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Web search restricted to specific domains (max 5, comma-separated)
|
|
43
|
+
.claude/skills/xai-search/scripts/search.sh --web --domains "example.com,docs.python.org" "query"
|
|
44
|
+
|
|
45
|
+
# X search filtered by handle (max 10, comma-separated)
|
|
46
|
+
.claude/skills/xai-search/scripts/search.sh --x --handles "elonmusk,openai" "query"
|
|
47
|
+
|
|
48
|
+
# X search with date range (ISO8601)
|
|
49
|
+
.claude/skills/xai-search/scripts/search.sh --x --from "2026-01-01" --to "2026-02-11" "query"
|
|
50
|
+
|
|
51
|
+
# Exclude domains from web search
|
|
52
|
+
.claude/skills/xai-search/scripts/search.sh --web --exclude-domains "reddit.com,quora.com" "query"
|
|
53
|
+
|
|
54
|
+
# Exclude X handles
|
|
55
|
+
.claude/skills/xai-search/scripts/search.sh --x --exclude-handles "spambot1,spambot2" "query"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## API Reference
|
|
59
|
+
|
|
60
|
+
- **Endpoint**: `https://api.x.ai/v1/responses`
|
|
61
|
+
- **Method**: POST
|
|
62
|
+
- **Auth**: Bearer token via `XAI_API_KEY`
|
|
63
|
+
- **Model**: `grok-4-1-fast` (default, can override with `--model`)
|
|
64
|
+
|
|
65
|
+
### Tool types
|
|
66
|
+
|
|
67
|
+
| Tool | Description | Use case |
|
|
68
|
+
|------|-------------|----------|
|
|
69
|
+
| `web_search` | Search the entire internet | Docs, news, products, general info |
|
|
70
|
+
| `x_search` | Search X posts, users, threads | Trends, sentiment, social updates |
|
|
71
|
+
|
|
72
|
+
### web_search parameters
|
|
73
|
+
- `allowed_domains` — max 5 domains to restrict search to
|
|
74
|
+
- `excluded_domains` — max 5 domains to exclude (mutually exclusive with allowed_domains)
|
|
75
|
+
- `enable_image_understanding` — analyze images found during browsing
|
|
76
|
+
|
|
77
|
+
### x_search parameters
|
|
78
|
+
- `allowed_x_handles` — max 10 handles to restrict search to
|
|
79
|
+
- `excluded_x_handles` — max 10 handles to exclude
|
|
80
|
+
- `from_date` / `to_date` — ISO8601 date filtering
|
|
81
|
+
- `enable_image_understanding` — analyze images in posts
|
|
82
|
+
- `enable_video_understanding` — analyze videos in posts
|
|
83
|
+
|
|
84
|
+
### Response format
|
|
85
|
+
The response includes `output` array with `message` items containing the search results, plus `citations` with source URLs. When `inline_citations: true` is set, the text includes inline markdown links.
|
|
86
|
+
|
|
87
|
+
## Internal Use
|
|
88
|
+
|
|
89
|
+
When you need current information about recent events, social media sentiment, trending topics, or up-to-date documentation, use this skill via the search script. The API handles the agentic loop internally — Grok will iteratively search and refine results before returning a comprehensive answer.
|
|
90
|
+
|
|
91
|
+
For combined searches (web + X), pass both tool types — the API will automatically determine which tool to use or use both based on the query.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# xAI Search — Grok Responses API (web_search + x_search)
|
|
3
|
+
# Usage: search.sh [--web|--x|--both] [options] "query"
|
|
4
|
+
#
|
|
5
|
+
# Options:
|
|
6
|
+
# --web Web search only
|
|
7
|
+
# --x X (Twitter) search only
|
|
8
|
+
# --both Both web + X (default)
|
|
9
|
+
# --model MODEL Override model (default: grok-4-1-fast)
|
|
10
|
+
# --domains D1,D2 Allowed domains for web_search (max 5)
|
|
11
|
+
# --exclude-domains Excluded domains for web_search (max 5)
|
|
12
|
+
# --handles H1,H2 Allowed X handles for x_search (max 10)
|
|
13
|
+
# --exclude-handles Excluded X handles for x_search (max 10)
|
|
14
|
+
# --from DATE Start date for x_search (ISO8601)
|
|
15
|
+
# --to DATE End date for x_search (ISO8601)
|
|
16
|
+
# --raw Output raw JSON response
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# --- Config ---
|
|
21
|
+
API_URL="https://api.x.ai/v1/responses"
|
|
22
|
+
DEFAULT_MODEL="grok-4-1-fast"
|
|
23
|
+
|
|
24
|
+
if [[ -z "${XAI_API_KEY:-}" ]]; then
|
|
25
|
+
echo "ERROR: XAI_API_KEY environment variable is not set" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# --- Parse args ---
|
|
30
|
+
MODE="both"
|
|
31
|
+
MODEL="$DEFAULT_MODEL"
|
|
32
|
+
DOMAINS=""
|
|
33
|
+
EXCLUDE_DOMAINS=""
|
|
34
|
+
HANDLES=""
|
|
35
|
+
EXCLUDE_HANDLES=""
|
|
36
|
+
FROM_DATE=""
|
|
37
|
+
TO_DATE=""
|
|
38
|
+
RAW=false
|
|
39
|
+
QUERY=""
|
|
40
|
+
|
|
41
|
+
while [[ $# -gt 0 ]]; do
|
|
42
|
+
case "$1" in
|
|
43
|
+
--web) MODE="web"; shift ;;
|
|
44
|
+
--x) MODE="x"; shift ;;
|
|
45
|
+
--both) MODE="both"; shift ;;
|
|
46
|
+
--model) MODEL="$2"; shift 2 ;;
|
|
47
|
+
--domains) DOMAINS="$2"; shift 2 ;;
|
|
48
|
+
--exclude-domains) EXCLUDE_DOMAINS="$2"; shift 2 ;;
|
|
49
|
+
--handles) HANDLES="$2"; shift 2 ;;
|
|
50
|
+
--exclude-handles) EXCLUDE_HANDLES="$2"; shift 2 ;;
|
|
51
|
+
--from) FROM_DATE="$2"; shift 2 ;;
|
|
52
|
+
--to) TO_DATE="$2"; shift 2 ;;
|
|
53
|
+
--raw) RAW=true; shift ;;
|
|
54
|
+
--*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
55
|
+
*) QUERY="$1"; shift ;;
|
|
56
|
+
esac
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
if [[ -z "$QUERY" ]]; then
|
|
60
|
+
echo "Usage: search.sh [--web|--x|--both] [options] \"query\"" >&2
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# --- Build tools array ---
|
|
65
|
+
build_web_tool() {
|
|
66
|
+
local tool='{"type":"web_search"'
|
|
67
|
+
local has_filters=false
|
|
68
|
+
local filters=""
|
|
69
|
+
|
|
70
|
+
if [[ -n "$DOMAINS" ]]; then
|
|
71
|
+
has_filters=true
|
|
72
|
+
# Convert comma-separated to JSON array
|
|
73
|
+
local arr
|
|
74
|
+
arr=$(echo "$DOMAINS" | jq -R 'split(",")')
|
|
75
|
+
filters="\"allowed_domains\":$arr"
|
|
76
|
+
elif [[ -n "$EXCLUDE_DOMAINS" ]]; then
|
|
77
|
+
has_filters=true
|
|
78
|
+
local arr
|
|
79
|
+
arr=$(echo "$EXCLUDE_DOMAINS" | jq -R 'split(",")')
|
|
80
|
+
filters="\"excluded_domains\":$arr"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [[ "$has_filters" == "true" ]]; then
|
|
84
|
+
tool="$tool,\"filters\":{$filters}"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
tool="$tool}"
|
|
88
|
+
echo "$tool"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
build_x_tool() {
|
|
92
|
+
local tool='{"type":"x_search"'
|
|
93
|
+
local parts=()
|
|
94
|
+
|
|
95
|
+
if [[ -n "$HANDLES" ]]; then
|
|
96
|
+
local arr
|
|
97
|
+
arr=$(echo "$HANDLES" | jq -R 'split(",")')
|
|
98
|
+
parts+=("\"allowed_x_handles\":$arr")
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
if [[ -n "$EXCLUDE_HANDLES" ]]; then
|
|
102
|
+
local arr
|
|
103
|
+
arr=$(echo "$EXCLUDE_HANDLES" | jq -R 'split(",")')
|
|
104
|
+
parts+=("\"excluded_x_handles\":$arr")
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
if [[ -n "$FROM_DATE" ]]; then
|
|
108
|
+
parts+=("\"from_date\":\"$FROM_DATE\"")
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if [[ -n "$TO_DATE" ]]; then
|
|
112
|
+
parts+=("\"to_date\":\"$TO_DATE\"")
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
if [[ ${#parts[@]} -gt 0 ]]; then
|
|
116
|
+
local joined
|
|
117
|
+
joined=$(printf ",%s" "${parts[@]}")
|
|
118
|
+
joined="${joined:1}" # remove leading comma
|
|
119
|
+
tool="$tool,$joined"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
tool="$tool}"
|
|
123
|
+
echo "$tool"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
TOOLS="["
|
|
127
|
+
case "$MODE" in
|
|
128
|
+
# web)
|
|
129
|
+
# TOOLS+="$(build_web_tool)"
|
|
130
|
+
# ;;
|
|
131
|
+
x|web|both)
|
|
132
|
+
TOOLS+="$(build_x_tool)"
|
|
133
|
+
;;
|
|
134
|
+
esac
|
|
135
|
+
TOOLS+="]"
|
|
136
|
+
|
|
137
|
+
# --- Build request body ---
|
|
138
|
+
BODY=$(jq -n \
|
|
139
|
+
--arg model "$MODEL" \
|
|
140
|
+
--arg query "$QUERY" \
|
|
141
|
+
--argjson tools "$TOOLS" \
|
|
142
|
+
'{
|
|
143
|
+
model: $model,
|
|
144
|
+
input: [{"role": "user", "content": $query}],
|
|
145
|
+
tools: $tools,
|
|
146
|
+
inline_citations: true
|
|
147
|
+
}')
|
|
148
|
+
|
|
149
|
+
# --- Make API call ---
|
|
150
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" "$API_URL" \
|
|
151
|
+
-H "Content-Type: application/json" \
|
|
152
|
+
-H "Authorization: Bearer $XAI_API_KEY" \
|
|
153
|
+
-d "$BODY")
|
|
154
|
+
|
|
155
|
+
# Extract HTTP status code (last line)
|
|
156
|
+
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
|
157
|
+
BODY_RESPONSE=$(echo "$RESPONSE" | sed '$d')
|
|
158
|
+
|
|
159
|
+
if [[ "$HTTP_CODE" -ne 200 ]]; then
|
|
160
|
+
echo "ERROR: API returned HTTP $HTTP_CODE" >&2
|
|
161
|
+
echo "$BODY_RESPONSE" >&2
|
|
162
|
+
exit 1
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
if [[ "$RAW" == "true" ]]; then
|
|
166
|
+
echo "$BODY_RESPONSE" | jq .
|
|
167
|
+
exit 0
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# --- Extract text content from response ---
|
|
171
|
+
# The Responses API returns output array with message items
|
|
172
|
+
echo "$BODY_RESPONSE" | jq -r '
|
|
173
|
+
# Extract text from output items
|
|
174
|
+
[.output[]? |
|
|
175
|
+
if .type == "message" then
|
|
176
|
+
(.content[]? | select(.type == "output_text") | .text)
|
|
177
|
+
else empty end
|
|
178
|
+
] | join("\n\n")
|
|
179
|
+
// "No results found."
|
|
180
|
+
'
|
|
181
|
+
|
|
182
|
+
# --- Extract citations if present ---
|
|
183
|
+
CITATIONS=$(echo "$BODY_RESPONSE" | jq -r '
|
|
184
|
+
[.output[]? |
|
|
185
|
+
if .type == "message" then
|
|
186
|
+
(.content[]? | select(.type == "output_text") | .annotations[]? |
|
|
187
|
+
select(.type == "url_citation") | "- [\(.title // .url)](\(.url))")
|
|
188
|
+
else empty end
|
|
189
|
+
] | unique | join("\n")
|
|
190
|
+
')
|
|
191
|
+
|
|
192
|
+
if [[ -n "$CITATIONS" ]]; then
|
|
193
|
+
echo ""
|
|
194
|
+
echo "---"
|
|
195
|
+
echo "Sources:"
|
|
196
|
+
echo "$CITATIONS"
|
|
197
|
+
fi
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2UI Response Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses A2UI JSONL blocks from agent responses.
|
|
5
|
+
* Supports streaming/progressive rendering via buffer management.
|
|
6
|
+
*
|
|
7
|
+
* Agent format (JSONL inside delimiters):
|
|
8
|
+
* [[a2ui]]
|
|
9
|
+
* {"surfaceUpdate":{"surfaceId":"main","components":[...]}}
|
|
10
|
+
* {"beginRendering":{"surfaceId":"main","root":"root"}}
|
|
11
|
+
* [[/a2ui]]
|
|
12
|
+
*
|
|
13
|
+
* Each line inside the block is a single A2UI v0.8 message object.
|
|
14
|
+
*/
|
|
15
|
+
import type { A2UIMessage } from "./types.js";
|
|
16
|
+
export interface ParsedA2UIBlock {
|
|
17
|
+
/** The parsed A2UI messages */
|
|
18
|
+
messages: A2UIMessage[];
|
|
19
|
+
/** The raw JSONL string */
|
|
20
|
+
jsonl: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ParsedA2UI {
|
|
23
|
+
/** Text parts with A2UI blocks removed */
|
|
24
|
+
textParts: string[];
|
|
25
|
+
/** Parsed A2UI blocks (each block = array of messages) */
|
|
26
|
+
blocks: ParsedA2UIBlock[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse a response string for [[a2ui]] ... [[/a2ui]] blocks.
|
|
30
|
+
* Returns the non-A2UI text parts and the parsed A2UI blocks.
|
|
31
|
+
*
|
|
32
|
+
* Handles incomplete blocks gracefully (for streaming support).
|
|
33
|
+
*
|
|
34
|
+
* @param text - Response text potentially containing A2UI blocks
|
|
35
|
+
* @param strict - If true, throw on unclosed blocks. If false, return partial data.
|
|
36
|
+
* @returns Parsed structure with text and A2UI blocks
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseA2UIBlocks(text: string, strict?: boolean): ParsedA2UI;
|
|
39
|
+
/**
|
|
40
|
+
* Remove all A2UI blocks from a text string.
|
|
41
|
+
* Useful for stripping A2UI before writing to memory or sending to non-A2UI channels.
|
|
42
|
+
*
|
|
43
|
+
* @param text - Text potentially containing A2UI blocks
|
|
44
|
+
* @returns Text with A2UI blocks removed
|
|
45
|
+
*/
|
|
46
|
+
export declare function stripA2UIBlocks(text: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a response contains A2UI blocks.
|
|
49
|
+
*
|
|
50
|
+
* @param text - Text to check
|
|
51
|
+
* @returns True if contains [[a2ui]] markers
|
|
52
|
+
*/
|
|
53
|
+
export declare function hasA2UIBlocks(text: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Check if a response has an incomplete A2UI block (for streaming buffering).
|
|
56
|
+
*
|
|
57
|
+
* @param text - Text to check
|
|
58
|
+
* @returns True if has opening [[a2ui]] without closing [[/a2ui]]
|
|
59
|
+
*/
|
|
60
|
+
export declare function hasIncompleteA2UIBlock(text: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Extract incomplete A2UI block for buffering.
|
|
63
|
+
* Used in streaming scenarios where agent response is sent in chunks.
|
|
64
|
+
*
|
|
65
|
+
* @param text - Text with incomplete block
|
|
66
|
+
* @returns The incomplete block content (from [[a2ui]] onwards), or null if none
|
|
67
|
+
*/
|
|
68
|
+
export declare function extractIncompleteBlock(text: string): string | null;
|
|
69
|
+
/**
|
|
70
|
+
* Get text before incomplete block (for sending as normal text).
|
|
71
|
+
*
|
|
72
|
+
* @param text - Text with incomplete block
|
|
73
|
+
* @returns Text before the incomplete block
|
|
74
|
+
*/
|
|
75
|
+
export declare function getTextBeforeIncompleteBlock(text: string): string;
|
|
76
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parseA2UIJsonl as n}from"./types.js";import{createLogger as t}from"../utils/logger.js";const e=t("A2UIParser");export function parseA2UIBlocks(t,o=!1){const s=[],r=[],i=t.split("\n");let u=!1,c=[],l=[];for(const t of i){const i=t.trim();if("[[a2ui]]"!==i)if("[[/a2ui]]"!==i)u?c.push(t):l.push(t);else{if(u){const t=c.join("\n").trim();try{const o=n(t);r.push({messages:o,jsonl:t}),e.debug(`Parsed A2UI block: ${o.length} messages`)}catch(n){const r=n instanceof Error?n.message:String(n);if(e.error(`Failed to parse A2UI JSONL: ${r}`),o)throw n;s.push(`[[a2ui]]\n${t}\n[[/a2ui]]`)}}u=!1,c=[]}else l.length>0&&(s.push(l.join("\n")),l=[]),u=!0,c=[]}if(u&&c.length>0){if(o)throw new Error("Unclosed [[a2ui]] block found");e.warn("Unclosed [[a2ui]] block found, treating as incomplete (streaming mode)"),s.push(`[[a2ui]]\n${c.join("\n")}`)}return l.length>0&&s.push(l.join("\n")),{textParts:s,blocks:r}}export function stripA2UIBlocks(n){return n.replace(/\[\[a2ui\]\][\s\S]*?\[\[\/a2ui\]\]/g,"").replace(/\n\n+/g,"\n\n").trim()}export function hasA2UIBlocks(n){return n.includes("[[a2ui]]")&&n.includes("[[/a2ui]]")}export function hasIncompleteA2UIBlock(n){const t=n.lastIndexOf("[[a2ui]]"),e=n.lastIndexOf("[[/a2ui]]");return t>=0&&(e<0||e<t)}export function extractIncompleteBlock(n){const t=n.lastIndexOf("[[a2ui]]");if(t<0)return null;const e=n.lastIndexOf("[[/a2ui]]");return e>=0&&e>t?null:n.slice(t)}export function getTextBeforeIncompleteBlock(n){const t=n.lastIndexOf("[[a2ui]]");if(t<0)return n;const e=n.lastIndexOf("[[/a2ui]]");return e>=0&&e>t?n:n.slice(0,t).trim()}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2UI (Agent-to-User Interface) Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Based on A2UI v0.8 spec (Stable) — aligned with @a2ui/lit official renderer.
|
|
5
|
+
*
|
|
6
|
+
* Wire format: JSONL (one JSON object per line), each with exactly one action key.
|
|
7
|
+
* Action keys: surfaceUpdate, beginRendering, dataModelUpdate, deleteSurface
|
|
8
|
+
*
|
|
9
|
+
* Key design principles:
|
|
10
|
+
* - Flat adjacency list (easy for LLMs to generate incrementally)
|
|
11
|
+
* - Framework-agnostic (renderer maps abstract types to native components)
|
|
12
|
+
* - Security-first (declarative data, not executable code)
|
|
13
|
+
*
|
|
14
|
+
* @see https://a2ui.org/specification/v0.8-a2ui/
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* A single component definition within a surfaceUpdate.
|
|
18
|
+
* Component types use PascalCase tagged unions (e.g. { Text: {...} }, { Button: {...} }).
|
|
19
|
+
*/
|
|
20
|
+
export interface A2UIComponentDef {
|
|
21
|
+
/** Unique ID within the surface */
|
|
22
|
+
id: string;
|
|
23
|
+
/** Tagged union: key is the component type, value is its props */
|
|
24
|
+
component: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* surfaceUpdate — creates or updates components on a surface.
|
|
28
|
+
*/
|
|
29
|
+
export interface A2UISurfaceUpdate {
|
|
30
|
+
surfaceUpdate: {
|
|
31
|
+
surfaceId: string;
|
|
32
|
+
components: A2UIComponentDef[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* beginRendering — tells the renderer to start displaying the surface.
|
|
37
|
+
*/
|
|
38
|
+
export interface A2UIBeginRendering {
|
|
39
|
+
beginRendering: {
|
|
40
|
+
surfaceId: string;
|
|
41
|
+
root: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* dataModelUpdate — updates the data model for data binding.
|
|
46
|
+
*/
|
|
47
|
+
export interface A2UIDataModelUpdate {
|
|
48
|
+
dataModelUpdate: {
|
|
49
|
+
surfaceId: string;
|
|
50
|
+
path: string;
|
|
51
|
+
value: unknown;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* deleteSurface — removes a surface entirely.
|
|
56
|
+
*/
|
|
57
|
+
export interface A2UIDeleteSurface {
|
|
58
|
+
deleteSurface: {
|
|
59
|
+
surfaceId: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Union of all v0.8 A2UI message types.
|
|
64
|
+
*/
|
|
65
|
+
export type A2UIMessage = A2UISurfaceUpdate | A2UIBeginRendering | A2UIDataModelUpdate | A2UIDeleteSurface;
|
|
66
|
+
/**
|
|
67
|
+
* Action key names for validation.
|
|
68
|
+
*/
|
|
69
|
+
export declare const A2UI_ACTION_KEYS: readonly ["beginRendering", "surfaceUpdate", "dataModelUpdate", "deleteSurface"];
|
|
70
|
+
/**
|
|
71
|
+
* A2UI payload sent from gateway to node via WebSocket.
|
|
72
|
+
* Contains JSONL messages as an array (pre-parsed from JSONL string).
|
|
73
|
+
*/
|
|
74
|
+
export interface A2UIWireMessage {
|
|
75
|
+
type: "a2ui_surface";
|
|
76
|
+
/** A2UI messages (parsed from JSONL) */
|
|
77
|
+
messages: A2UIMessage[];
|
|
78
|
+
/** Raw JSONL string (for clients that use @a2ui/lit processor directly) */
|
|
79
|
+
jsonl: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A2UI user action sent from node back to gateway via WebSocket.
|
|
83
|
+
*/
|
|
84
|
+
export interface A2UIUserAction {
|
|
85
|
+
/** Unique action ID (UUID) */
|
|
86
|
+
id: string;
|
|
87
|
+
/** Action name (from Button.action.event.name) */
|
|
88
|
+
name: string;
|
|
89
|
+
/** Surface ID where action occurred */
|
|
90
|
+
surfaceId: string;
|
|
91
|
+
/** Component ID that triggered the action */
|
|
92
|
+
sourceComponentId: string;
|
|
93
|
+
/** ISO 8601 timestamp */
|
|
94
|
+
timestamp: string;
|
|
95
|
+
/** Resolved context data (from data model paths or literals) */
|
|
96
|
+
context?: Record<string, unknown>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A2UI action message from node to gateway.
|
|
100
|
+
*/
|
|
101
|
+
export interface A2UIActionMessage {
|
|
102
|
+
type: "a2ui_action";
|
|
103
|
+
/** Chat ID for routing */
|
|
104
|
+
chatId: string;
|
|
105
|
+
/** User action payload */
|
|
106
|
+
userAction: A2UIUserAction;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Standard A2UI v0.8 component types (PascalCase).
|
|
110
|
+
* These are the components supported by @a2ui/lit renderer.
|
|
111
|
+
*/
|
|
112
|
+
export declare const A2UI_COMPONENT_TYPES: readonly ["Column", "Row", "Card", "Divider", "Tabs", "Modal", "List", "Text", "Image", "Icon", "AudioPlayer", "Video", "Button", "TextField", "CheckBox", "MultipleChoice", "Slider", "DateTimeInput"];
|
|
113
|
+
export type A2UIComponentType = (typeof A2UI_COMPONENT_TYPES)[number] | string;
|
|
114
|
+
/**
|
|
115
|
+
* Validate JSONL string and return parsed messages.
|
|
116
|
+
* Throws on invalid input with descriptive error messages.
|
|
117
|
+
*/
|
|
118
|
+
export declare function parseA2UIJsonl(jsonl: string): A2UIMessage[];
|
|
119
|
+
/**
|
|
120
|
+
* Build a simple text surface JSONL (for testing / quick surfaces).
|
|
121
|
+
*
|
|
122
|
+
* @param text - Text content to display
|
|
123
|
+
* @param surfaceId - Surface ID (default: "main")
|
|
124
|
+
* @returns JSONL string ready to send
|
|
125
|
+
*/
|
|
126
|
+
export declare function buildTextSurfaceJsonl(text: string, surfaceId?: string): string;
|
|
127
|
+
/**
|
|
128
|
+
* Format user action as readable text for agent consumption.
|
|
129
|
+
*
|
|
130
|
+
* @param action - A2UI user action
|
|
131
|
+
* @returns Formatted string like "[A2UI Action] submit on surface booking ..."
|
|
132
|
+
*/
|
|
133
|
+
export declare function formatUserActionForAgent(action: A2UIUserAction): string;
|
|
134
|
+
/**
|
|
135
|
+
* Validate that a message array contains required rendering messages.
|
|
136
|
+
*
|
|
137
|
+
* @param messages - Array of A2UI messages
|
|
138
|
+
* @returns Validation result with surfaceIds found
|
|
139
|
+
*/
|
|
140
|
+
export declare function validateA2UIMessages(messages: A2UIMessage[]): {
|
|
141
|
+
valid: boolean;
|
|
142
|
+
surfaceIds: Set<string>;
|
|
143
|
+
hasUpdates: boolean;
|
|
144
|
+
hasRendering: boolean;
|
|
145
|
+
error?: string;
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=types.d.ts.map
|