2026-02-26T00-00-18_auto_memory/memories.db-wal, memory/memories.db-wal
This commit is contained in:
parent
442a178d23
commit
80233a715d
@ -23638,3 +23638,28 @@ hint: See the 'Note about fast-forwards' in 'git push --help' for details.
|
||||
[2m23:27:43[0m [36mINFO [0m [memory] Chunked memory saved [2m{"groupId":"84bcb13d-3767-44a0-a967-1d38f1ceb3bd","chunkCount":0}[0m
|
||||
[2m23:27:43[0m [36mINFO [0m [watcher] Ingested memory file [2m{"path":"/home/nicholai/.agents/memory/2026-02-24-ad-prediction-system-proposal-review.md","chunks":1,"sections":1,"filename":"2026-02-24-ad-prediction-system-proposal-review"}[0m
|
||||
[2m23:27:43[0m [36mINFO [0m [daemon] Imported existing memory files [2m{"files":142,"chunks":320}[0m
|
||||
[2m23:27:48[0m [36mINFO [0m [git] Auto-committed [2m{"message":"2026-02-25T23-27-48_auto_memory/memories.db-wal, memory/memories.db-wal, me","filesChanged":7}[0m
|
||||
[2m23:28:16[0m [36mINFO [0m [secrets] exec_with_secrets completed [2m{"name":"OPENROUTER_API_KEY","code":0}[0m
|
||||
[2m23:28:24[0m [36mINFO [0m [secrets] exec_with_secrets completed [2m{"name":"OPENROUTER_API_KEY","code":0}[0m
|
||||
[2m23:28:49[0m [36mINFO [0m [secrets] exec_with_secrets completed [2m{"name":"OPENROUTER_API_KEY","code":0}[0m
|
||||
[2m23:29:21[0m [36mINFO [0m [secrets] Secret deleted [2m{"name":"OPENROUTER_API_KEY"}[0m
|
||||
[2m23:29:22[0m [36mINFO [0m [secrets] Secret stored [2m{"name":"OPENROUTER_API_KEY"}[0m
|
||||
[2m23:29:42[0m [36mINFO [0m [secrets] exec_with_secrets completed [2m{"name":"OPENROUTER_API_KEY","code":0}[0m
|
||||
[2m23:30:34[0m [36mINFO [0m [secrets] exec_with_secrets completed [2m{"name":"OPENROUTER_API_KEY","code":0}[0m
|
||||
[2m23:30:56[0m [36mINFO [0m [secrets] exec_with_secrets completed [2m{"name":"OPENROUTER_API_KEY","code":0}[0m
|
||||
[2m23:32:51[0m [36mINFO [0m [git] Git push [2m{"commits":789}[0m
|
||||
[2m23:33:03[0m [36mINFO [0m [skills] Fetching skills.sh catalog
|
||||
[2m23:33:03[0m [36mINFO [0m [skills] Fetching ClawHub catalog
|
||||
[2m23:33:03[0m [36mINFO [0m [skills] Cached 600 skills
|
||||
[2m23:33:04[0m [31mERROR[0m [skills] ClawHub catalog fetch failed
|
||||
[31mError: ClawHub returned 429[0m
|
||||
[2m23:37:42[0m [36mINFO [0m [git] Git push [2m{"commits":789}[0m
|
||||
[2m23:42:42[0m [36mINFO [0m [git] Git push [2m{"commits":789}[0m
|
||||
[2m23:47:42[0m [36mINFO [0m [git] Git push [2m{"commits":789}[0m
|
||||
[2m23:52:42[0m [36mINFO [0m [git] Git push [2m{"commits":789}[0m
|
||||
[2m23:57:42[0m [36mINFO [0m [git] Git push [2m{"commits":789}[0m
|
||||
[2m00:00:13[0m [36mINFO [0m [scheduler] Executing task: Find a bug and fix it [2m{"taskId":"51f5d597-5c2d-4282-bad1-ac9010862650","runId":"47c819d5-bc20-46de-9c09-afe0ec264eec","harness":"claude-code"}[0m
|
||||
[2m00:00:13[0m [36mINFO [0m [scheduler] Spawning claude-code [2m{"bin":"/home/nicholai/.local/share/../bin/claude","cwd":"/home/nicholai/signet/signetai/"}[0m
|
||||
[2m00:00:13[0m [36mINFO [0m [watcher] File changed [2m{"path":"/home/nicholai/.agents/memory/memories.db-wal"}[0m
|
||||
[2m00:00:13[0m [36mINFO [0m [scheduler] Task Find a bug and fix it failed [2m{"taskId":"51f5d597-5c2d-4282-bad1-ac9010862650","runId":"47c819d5-bc20-46de-9c09-afe0ec264eec","exitCode":1,"timedOut":false}[0m
|
||||
[2m00:00:13[0m [36mINFO [0m [watcher] File changed [2m{"path":"/home/nicholai/.agents/memory/memories.db-wal"}[0m
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"secrets": {
|
||||
"OPENROUTER_API_KEY": {
|
||||
"ciphertext": "WgC40fXcR4Z/fR+zMeVH8Rl8U5y/BokoGaLeI9Ak4DKoqMS5GYvJ0C1HHw==",
|
||||
"created": "2026-02-18T21:15:08.588Z",
|
||||
"updated": "2026-02-18T21:15:08.588Z"
|
||||
},
|
||||
"GOOGLE_AI_API_KEY": {
|
||||
"ciphertext": "1cyAwWgmCqZxQpVGTvPCyyv1C6mt/8YzPfFrTK1crKE2lupmnhCInm0d4PwJeBMAPF6XQbrJZgv4aMZT9utO70+hSkXIZcKPnA4iZYcRew==",
|
||||
"created": "2026-02-21T07:40:25.875Z",
|
||||
@ -45,6 +40,11 @@
|
||||
"ciphertext": "xN8VWOFvMNgmUuCkM44MRFsNf9atDvCvJm6qBis5+FBpJrMlRR9JpmkhSSDd6SZx11/N336IUXQM8LcxtprBiSk6GekDk5COmk3qBin+ry4rF+RZ2NQ2mj6WTnxzxfMgtaK9Gne482zlftCs7CzLLmKcyCW6vrpZzNI8t9CEeThhMJnNcVKbTQHtk/9WD+10bs+jacZe7vsM8RLRsMOI2cUJdiKRCO6b7/o8hzPuCmziUaR54jO0bxdlFBHzJH8hiURlNdZ3ZwQBiWIEcDK15L5FrJSl4hu2oAMCDHTBb5mDzl35fZsH6SPzWhA4FSlvE7SDytOcPQ==",
|
||||
"created": "2026-02-24T11:00:59.366Z",
|
||||
"updated": "2026-02-24T11:05:51.951Z"
|
||||
},
|
||||
"OPENROUTER_API_KEY": {
|
||||
"ciphertext": "yCu2nwjkBU+jCYRy9pKDX//fHjlQPaJiWFG5a9sCRXCzGki4oa+2nULx1nzCrHHBaNCne/sXew2x1XhV3No0aVux4mW2kSwcnMzbu6DJM61ENWA9ZJi5R18xVOeQAl9Bs2uI9VuhIvH9Sr9IJWDX91c=",
|
||||
"created": "2026-02-25T23:29:22.944Z",
|
||||
"updated": "2026-02-25T23:29:22.944Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
100
scripts/speak.sh
100
scripts/speak.sh
@ -1,40 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
API_KEY=$(signet secret get OPENROUTER_API_KEY 2>/dev/null)
|
||||
VOICE="${VOICE:-ash}"
|
||||
FORMAT="${FORMAT:-wav}"
|
||||
TEXT="$*"
|
||||
DAEMON="http://localhost:3850"
|
||||
TMPRAW=$(mktemp /tmp/speak-XXXX.raw)
|
||||
TMPWAV=$(mktemp /tmp/speak-XXXX.wav)
|
||||
trap "rm -f $TMPRAW $TMPWAV" EXIT
|
||||
|
||||
if [ -z "$TEXT" ]; then
|
||||
echo "Usage: speak.sh <text to speak>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RESPONSE=$(curl -s https://openrouter.ai/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
# The inner command streams the SSE response, extracts base64 audio chunks,
|
||||
# concatenates and decodes them to raw PCM16.
|
||||
# We use jq to safely embed the text into the JSON payload.
|
||||
read -r -d '' INNER_CMD << 'INNEREOF' || true
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg text "$SPEAK_TEXT" \
|
||||
--arg voice "$SPEAK_VOICE" \
|
||||
'{
|
||||
model: "openai/gpt-audio-mini",
|
||||
modalities: ["text", "audio"],
|
||||
audio: { voice: $voice, format: "pcm16" },
|
||||
stream: true,
|
||||
messages: [{ role: "user", content: $text }]
|
||||
}')
|
||||
|
||||
curl -sN https://openrouter.ai/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(jq -n \
|
||||
--arg text "$TEXT" \
|
||||
--arg voice "$VOICE" \
|
||||
--arg fmt "$FORMAT" \
|
||||
'{
|
||||
model: "openai/gpt-audio-mini",
|
||||
modalities: ["text", "audio"],
|
||||
audio: { voice: $voice, format: $fmt },
|
||||
messages: [{ role: "user", content: $text }]
|
||||
}')")
|
||||
-d "$PAYLOAD" | \
|
||||
while IFS= read -r line; do
|
||||
# Strip "data: " prefix from SSE
|
||||
line="${line#data: }"
|
||||
[ -z "$line" ] && continue
|
||||
[ "$line" = "[DONE]" ] && continue
|
||||
# Extract audio data chunk if present
|
||||
chunk=$(echo "$line" | jq -r '.choices[0].delta.audio.data // empty' 2>/dev/null)
|
||||
[ -n "$chunk" ] && printf '%s' "$chunk"
|
||||
done
|
||||
INNEREOF
|
||||
|
||||
# Extract audio data
|
||||
AUDIO_DATA=$(echo "$RESPONSE" | jq -r '.choices[0].message.audio.data // empty')
|
||||
# Execute the streaming command via daemon's exec_with_secrets.
|
||||
# We pass SPEAK_TEXT and SPEAK_VOICE as additional env vars through the secrets map.
|
||||
# The exec endpoint injects OPENROUTER_API_KEY; we also set our custom vars in the command.
|
||||
INNER_WITH_VARS="export SPEAK_TEXT=$(printf '%q' "$TEXT"); export SPEAK_VOICE=$(printf '%q' "$VOICE"); $INNER_CMD"
|
||||
|
||||
if [ -z "$AUDIO_DATA" ]; then
|
||||
echo "Error: No audio in response"
|
||||
echo "$RESPONSE" | jq '.error // .choices[0].message.content // .' 2>/dev/null
|
||||
EXEC_RESPONSE=$(curl -s "$DAEMON/api/secrets/OPENROUTER_API_KEY/exec" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(jq -n --arg cmd "$INNER_WITH_VARS" '{ command: $cmd }')")
|
||||
|
||||
# Extract the concatenated base64 audio from stdout
|
||||
B64_AUDIO=$(echo "$EXEC_RESPONSE" | jq -r '.stdout // empty')
|
||||
|
||||
if [ -z "$B64_AUDIO" ]; then
|
||||
STDERR=$(echo "$EXEC_RESPONSE" | jq -r '.stderr // empty')
|
||||
echo "Error: No audio data received"
|
||||
[ -n "$STDERR" ] && echo "stderr: $STDERR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMPFILE=$(mktemp /tmp/speak-XXXX.wav)
|
||||
trap "rm -f $TMPFILE" EXIT
|
||||
echo "$AUDIO_DATA" | base64 -d > "$TMPFILE"
|
||||
ffplay -nodisp -autoexit -loglevel quiet "$TMPFILE"
|
||||
# Decode base64 to raw PCM16
|
||||
echo "$B64_AUDIO" | base64 -d > "$TMPRAW" 2>/dev/null
|
||||
|
||||
RAWSIZE=$(stat -c%s "$TMPRAW" 2>/dev/null || stat -f%z "$TMPRAW" 2>/dev/null)
|
||||
if [ "$RAWSIZE" -lt 100 ]; then
|
||||
echo "Error: Audio data too small ($RAWSIZE bytes)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wrap raw PCM16 in a WAV header (24kHz, mono, 16-bit LE)
|
||||
# WAV header is 44 bytes
|
||||
DATASIZE=$RAWSIZE
|
||||
FILESIZE=$((DATASIZE + 36))
|
||||
{
|
||||
printf 'RIFF'
|
||||
printf "$(printf '\\x%02x\\x%02x\\x%02x\\x%02x' $((FILESIZE & 0xFF)) $(((FILESIZE >> 8) & 0xFF)) $(((FILESIZE >> 16) & 0xFF)) $(((FILESIZE >> 24) & 0xFF)))"
|
||||
printf 'WAVEfmt '
|
||||
printf '\x10\x00\x00\x00' # chunk size 16
|
||||
printf '\x01\x00' # PCM format
|
||||
printf '\x01\x00' # mono
|
||||
printf '\xc0\x5d\x00\x00' # 24000 Hz sample rate
|
||||
printf '\x80\xbb\x00\x00' # byte rate (24000 * 2)
|
||||
printf '\x02\x00' # block align
|
||||
printf '\x10\x00' # 16 bits per sample
|
||||
printf 'data'
|
||||
printf "$(printf '\\x%02x\\x%02x\\x%02x\\x%02x' $((DATASIZE & 0xFF)) $(((DATASIZE >> 8) & 0xFF)) $(((DATASIZE >> 16) & 0xFF)) $(((DATASIZE >> 24) & 0xFF)))"
|
||||
cat "$TMPRAW"
|
||||
} > "$TMPWAV"
|
||||
|
||||
ffplay -nodisp -autoexit -loglevel quiet "$TMPWAV"
|
||||
|
||||
BIN
signet-speak.skill
Normal file
BIN
signet-speak.skill
Normal file
Binary file not shown.
36
skills/signet-speak/SKILL.md
Normal file
36
skills/signet-speak/SKILL.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
name: signet-speak
|
||||
description: Speak audibly to the user via text-to-speech using OpenAI's gpt-audio-mini model through OpenRouter. Use when the agent wants to talk out loud, greet the user verbally, deliver information audibly, or when spoken audio output would enhance the interaction. Also triggers when the user asks the agent to "say something", "speak", "talk to me", "use your voice", or "read this aloud".
|
||||
---
|
||||
|
||||
# Signet Speak
|
||||
|
||||
Speak out loud via TTS. Uses OpenRouter's `openai/gpt-audio-mini` with Signet daemon's `exec_with_secrets` for secure API key injection.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
~/.agents/skills/signet-speak/scripts/speak.sh "whatever you want to say"
|
||||
```
|
||||
|
||||
Override voice (default: ash):
|
||||
|
||||
```bash
|
||||
VOICE=coral ~/.agents/skills/signet-speak/scripts/speak.sh "hello there"
|
||||
```
|
||||
|
||||
## Voices
|
||||
|
||||
alloy, **ash** (default), ballad, coral, echo, fable, onyx, nova, sage, shimmer, verse, marin, cedar
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Signet daemon running (`signet daemon start`)
|
||||
- `OPENROUTER_API_KEY` in Signet secrets
|
||||
- `ffplay`, `jq`, `curl` on PATH
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No audio data**: Check daemon (`signet status`), verify API key
|
||||
- **401 error**: Re-store key with `signet secret put OPENROUTER_API_KEY`
|
||||
- **No sound from speakers**: Test with `ffplay -f lavfi -i sine=f=440:d=1 -nodisp -autoexit`
|
||||
88
skills/signet-speak/scripts/speak.sh
Executable file
88
skills/signet-speak/scripts/speak.sh
Executable file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VOICE="${VOICE:-ash}"
|
||||
TEXT="$*"
|
||||
DAEMON="http://localhost:3850"
|
||||
TMPRAW=$(mktemp /tmp/speak-XXXX.raw)
|
||||
TMPWAV=$(mktemp /tmp/speak-XXXX.wav)
|
||||
trap "rm -f $TMPRAW $TMPWAV" EXIT
|
||||
|
||||
if [ -z "$TEXT" ]; then
|
||||
echo "Usage: speak.sh <text to speak>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build the inner command that runs with OPENROUTER_API_KEY injected by the daemon
|
||||
read -r -d '' INNER_CMD << 'INNEREOF' || true
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg text "$SPEAK_TEXT" \
|
||||
--arg voice "$SPEAK_VOICE" \
|
||||
'{
|
||||
model: "openai/gpt-audio-mini",
|
||||
modalities: ["text", "audio"],
|
||||
audio: { voice: $voice, format: "pcm16" },
|
||||
stream: true,
|
||||
messages: [{ role: "user", content: $text }]
|
||||
}')
|
||||
|
||||
curl -sN https://openrouter.ai/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" | \
|
||||
while IFS= read -r line; do
|
||||
line="${line#data: }"
|
||||
[ -z "$line" ] && continue
|
||||
[ "$line" = "[DONE]" ] && continue
|
||||
chunk=$(echo "$line" | jq -r '.choices[0].delta.audio.data // empty' 2>/dev/null)
|
||||
[ -n "$chunk" ] && printf '%s' "$chunk"
|
||||
done
|
||||
INNEREOF
|
||||
|
||||
# Inject text and voice as shell-safe env vars, then run the inner command
|
||||
# through the Signet daemon's exec_with_secrets endpoint
|
||||
INNER_WITH_VARS="export SPEAK_TEXT=$(printf '%q' "$TEXT"); export SPEAK_VOICE=$(printf '%q' "$VOICE"); $INNER_CMD"
|
||||
|
||||
EXEC_RESPONSE=$(curl -s "$DAEMON/api/secrets/OPENROUTER_API_KEY/exec" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(jq -n --arg cmd "$INNER_WITH_VARS" '{ command: $cmd }')")
|
||||
|
||||
# Extract concatenated base64 audio from stdout
|
||||
B64_AUDIO=$(echo "$EXEC_RESPONSE" | jq -r '.stdout // empty')
|
||||
|
||||
if [ -z "$B64_AUDIO" ]; then
|
||||
STDERR=$(echo "$EXEC_RESPONSE" | jq -r '.stderr // empty')
|
||||
echo "Error: No audio data received"
|
||||
[ -n "$STDERR" ] && echo "stderr: $STDERR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Decode base64 to raw PCM16
|
||||
echo "$B64_AUDIO" | base64 -d > "$TMPRAW" 2>/dev/null
|
||||
|
||||
RAWSIZE=$(stat -c%s "$TMPRAW" 2>/dev/null || stat -f%z "$TMPRAW" 2>/dev/null)
|
||||
if [ "$RAWSIZE" -lt 100 ]; then
|
||||
echo "Error: Audio data too small ($RAWSIZE bytes)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wrap raw PCM16 in a WAV header (24kHz, mono, 16-bit LE)
|
||||
DATASIZE=$RAWSIZE
|
||||
FILESIZE=$((DATASIZE + 36))
|
||||
{
|
||||
printf 'RIFF'
|
||||
printf "$(printf '\\x%02x\\x%02x\\x%02x\\x%02x' $((FILESIZE & 0xFF)) $(((FILESIZE >> 8) & 0xFF)) $(((FILESIZE >> 16) & 0xFF)) $(((FILESIZE >> 24) & 0xFF)))"
|
||||
printf 'WAVEfmt '
|
||||
printf '\x10\x00\x00\x00' # chunk size 16
|
||||
printf '\x01\x00' # PCM format
|
||||
printf '\x01\x00' # mono
|
||||
printf '\xc0\x5d\x00\x00' # 24000 Hz
|
||||
printf '\x80\xbb\x00\x00' # byte rate (24000 * 2)
|
||||
printf '\x02\x00' # block align
|
||||
printf '\x10\x00' # 16 bits per sample
|
||||
printf 'data'
|
||||
printf "$(printf '\\x%02x\\x%02x\\x%02x\\x%02x' $((DATASIZE & 0xFF)) $(((DATASIZE >> 8) & 0xFF)) $(((DATASIZE >> 16) & 0xFF)) $(((DATASIZE >> 24) & 0xFF)))"
|
||||
cat "$TMPRAW"
|
||||
} > "$TMPWAV"
|
||||
|
||||
ffplay -nodisp -autoexit -loglevel quiet "$TMPWAV"
|
||||
Loading…
x
Reference in New Issue
Block a user