Skip to content

Commit 072a4b9

Browse files
Merge branch 'jaredlockhart:main' into main
2 parents 631cb33 + a802863 commit 072a4b9

62 files changed

Lines changed: 1892 additions & 3053 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ OLLAMA_MODEL="gpt-oss:20b" # Single model for all penny agents
1313
# Penny-team Quality agent (optional — only registers if set)
1414
# OLLAMA_BACKGROUND_MODEL="nemotron-3-nano:30b"
1515

16-
# Perplexity Configuration
17-
PERPLEXITY_API_KEY="pplx-your-api-key-here"
18-
1916
# Database
2017
DB_PATH="/penny/data/penny/penny.db"
2118

CLAUDE.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## What Is Penny
44

5-
Penny is a local-first AI agent that communicates via Signal, Discord, or a Firefox browser extension. Users send messages, Penny searches the web via Perplexity, reasons using Ollama (local LLM), and replies in a casual, relaxed style. It runs in Docker with host networking.
5+
Penny is a local-first AI agent that communicates via Signal, Discord, or a Firefox browser extension. Users send messages, Penny searches the web through the browser extension, reasons using Ollama (local LLM), and replies in a casual, relaxed style. It runs in Docker with host networking.
66

77
Penny is single-user — a personal assistant deployed locally for one person. Multiple devices (Signal phone, browser instances) connect as different devices of the same user, sharing a single conversation history.
88

@@ -132,7 +132,7 @@ On the host, dev tool commands run via `docker compose run --rm` in a temporary
132132

133133
`make prod` starts the penny service only (skips `docker-compose.override.yml` and the `team` profile). The watcher container handles auto-deploy when running the full stack via `make up`.
134134

135-
Prerequisites: signal-cli-rest-api on :8080 (for Signal), Ollama on :11434, Perplexity API key in .env.
135+
Prerequisites: signal-cli-rest-api on :8080 (for Signal), Ollama on :11434, browser extension for web search.
136136

137137
## CI
138138

@@ -167,14 +167,11 @@ GitHub Actions runs `make check` (format, lint, typecheck, tests) on every push
167167
- `OLLAMA_RETRY_DELAY`: Delay in seconds between retries (default: 0.5)
168168

169169
**API Keys**:
170-
- `PERPLEXITY_API_KEY`: API key for web search
171-
- `SERPER_API_KEY`: API key for Serper image search (optional; if unset, notifications won't include images)
172170
- `CLAUDE_CODE_OAUTH_TOKEN`: OAuth token for Claude CLI Max plan (agent containers, via `claude setup-token`)
173171
- `FASTMAIL_API_TOKEN`: API token for Fastmail JMAP email search (optional, enables `/email` command)
174172
- `ZOHO_API_ID`: Zoho OAuth client ID (optional, enables `/zoho` command)
175173
- `ZOHO_API_SECRET`: Zoho OAuth client secret (optional, enables `/zoho` command)
176174
- `ZOHO_REFRESH_TOKEN`: Zoho OAuth refresh token (optional, enables `/zoho` command) — obtain via [OAuth flow](https://www.zoho.com/mail/help/api/using-oauth-2.html)
177-
- `NEWS_API_KEY`: API key for TheNewsAPI.com (optional; enables news search tool for chat and thinking agents)
178175
**GitHub App** (required for agent containers and `/bug` command):
179176
- `GITHUB_APP_ID`: GitHub App ID for authenticated API access
180177
- `GITHUB_APP_PRIVATE_KEY_PATH`: Path to GitHub App private key file

README.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ See [docs/browser-extension-architecture.md](docs/browser-extension-architecture
167167
1. **For Signal**: [signal-cli-rest-api](https://github.com/bbernhard/signal-cli-rest-api) running on host (port 8080)
168168
2. **For Discord**: Discord bot token and channel ID
169169
3. **[Ollama](https://ollama.com)** running on host (port 11434)
170-
4. **[Perplexity API key](https://www.perplexity.ai)** (for web search)
170+
4. **Browser extension** (for web search and page reading)
171171
5. Docker & Docker Compose installed
172172

173173
### Quick Start
@@ -228,9 +228,6 @@ BROWSER_PORT=9090
228228
OLLAMA_API_URL="http://host.docker.internal:11434"
229229
OLLAMA_MODEL="gpt-oss:20b" # Single model for all agents
230230

231-
# Perplexity Configuration
232-
PERPLEXITY_API_KEY="your-api-key"
233-
234231
# Database & Logging
235232
DB_PATH="/penny/data/penny/penny.db"
236233
LOG_LEVEL="INFO"
@@ -268,9 +265,6 @@ Penny auto-detects which channel to use based on configured credentials:
268265
- `OLLAMA_RETRY_DELAY`: Delay in seconds between retries (default: 0.5)
269266

270267
**API Keys:**
271-
- `PERPLEXITY_API_KEY`: API key for web search
272-
- `SERPER_API_KEY`: API key for Serper image search (optional; if unset, notifications won't include images)
273-
- `NEWS_API_KEY`: API key for TheNewsAPI.com (optional; enables news search tool)
274268
- `FASTMAIL_API_TOKEN`: API token for Fastmail JMAP email search (optional, enables `/email`)
275269

276270
**GitHub App** (optional, enables `/bug` and `/feature`; required for agent containers):

browser/sidebar/sidebar.css

Lines changed: 245 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ body {
9393
align-items: center;
9494
gap: 6px;
9595
padding: 7px 10px;
96-
border-bottom: 1px solid var(--border);
9796
}
9897

9998
.nav-icon {
@@ -152,6 +151,56 @@ body {
152151
color: var(--accent);
153152
}
154153

154+
/* --- Main tabs (Chat / Schedules) --- */
155+
156+
#main-tabs {
157+
display: flex;
158+
}
159+
160+
.main-tab {
161+
display: inline-flex;
162+
align-items: center;
163+
justify-content: center;
164+
gap: 5px;
165+
flex: 1;
166+
padding: 7px 4px;
167+
border: none;
168+
background: none;
169+
color: var(--text-muted);
170+
font-size: 12px;
171+
font-weight: 500;
172+
cursor: pointer;
173+
border-bottom: 2px solid transparent;
174+
margin-bottom: -1px;
175+
}
176+
177+
.main-tab:hover {
178+
color: var(--text-primary);
179+
}
180+
181+
.main-tab.active {
182+
color: var(--accent);
183+
border-bottom-color: var(--accent);
184+
}
185+
186+
/* --- Conversation panel --- */
187+
188+
#conversation-panel {
189+
display: flex;
190+
flex-direction: column;
191+
flex: 1;
192+
min-height: 0;
193+
}
194+
195+
/* --- Schedules panel --- */
196+
197+
#schedules-panel {
198+
display: flex;
199+
flex-direction: column;
200+
flex: 1;
201+
min-height: 0;
202+
}
203+
155204
/* --- Messages wrapper --- */
156205

157206
#messages-wrapper {
@@ -673,6 +722,201 @@ body {
673722
background: var(--accent-hover);
674723
}
675724

725+
/* --- Schedules panel --- */
726+
727+
.schedules-list {
728+
flex: 1;
729+
overflow-y: auto;
730+
padding: 4px 0;
731+
}
732+
733+
.schedule-row {
734+
padding: 8px 16px;
735+
cursor: pointer;
736+
}
737+
738+
.schedule-row:hover {
739+
background: var(--bg-secondary);
740+
}
741+
742+
.schedule-header {
743+
display: flex;
744+
align-items: center;
745+
gap: 8px;
746+
}
747+
748+
.schedule-timing {
749+
font-size: 11px;
750+
font-weight: 600;
751+
color: var(--accent);
752+
flex-shrink: 0;
753+
}
754+
755+
.schedule-prompt {
756+
flex: 1;
757+
font-size: 13px;
758+
color: var(--text-primary);
759+
overflow: hidden;
760+
text-overflow: ellipsis;
761+
white-space: nowrap;
762+
}
763+
764+
.schedule-delete {
765+
border: none;
766+
background: none;
767+
color: var(--text-muted);
768+
cursor: pointer;
769+
padding: 2px 6px;
770+
border-radius: 4px;
771+
font-size: 16px;
772+
line-height: 1;
773+
flex-shrink: 0;
774+
}
775+
776+
.schedule-delete:hover {
777+
color: var(--status-err-text);
778+
background: var(--status-err-bg);
779+
}
780+
781+
.schedule-detail {
782+
display: none;
783+
padding-top: 8px;
784+
}
785+
786+
.schedule-row.expanded .schedule-detail {
787+
display: block;
788+
}
789+
790+
.schedule-row.expanded .schedule-prompt {
791+
white-space: normal;
792+
overflow: visible;
793+
}
794+
795+
.schedule-cron {
796+
font-size: 10px;
797+
font-family: monospace;
798+
color: var(--text-muted);
799+
margin-bottom: 6px;
800+
}
801+
802+
.schedule-edit-input {
803+
width: 100%;
804+
padding: 6px 10px;
805+
border: 1px solid var(--bg-tertiary);
806+
border-radius: 6px;
807+
background: var(--bg-secondary);
808+
color: var(--text-primary);
809+
font-family: inherit;
810+
font-size: 13px;
811+
outline: none;
812+
resize: vertical;
813+
min-height: 32px;
814+
}
815+
816+
.schedule-edit-input:focus {
817+
border-color: var(--accent);
818+
}
819+
820+
.schedule-save {
821+
margin-top: 6px;
822+
padding: 4px 12px;
823+
border: none;
824+
border-radius: 6px;
825+
background: var(--accent);
826+
color: var(--accent-text);
827+
font-size: 12px;
828+
font-weight: 600;
829+
cursor: pointer;
830+
}
831+
832+
.schedule-save:hover {
833+
background: var(--accent-hover);
834+
}
835+
836+
.schedules-add {
837+
display: flex;
838+
gap: 8px;
839+
padding: 12px 16px;
840+
border-top: 1px solid var(--border);
841+
}
842+
843+
.schedules-add input {
844+
flex: 1;
845+
padding: 8px 12px;
846+
border: 1px solid var(--bg-tertiary);
847+
border-radius: 8px;
848+
background: var(--bg-secondary);
849+
color: var(--text-primary);
850+
font-family: inherit;
851+
font-size: 13px;
852+
outline: none;
853+
}
854+
855+
.schedules-add input:focus {
856+
border-color: var(--accent);
857+
}
858+
859+
.schedules-add button {
860+
padding: 8px 12px;
861+
border: none;
862+
border-radius: 8px;
863+
background: var(--accent);
864+
color: var(--accent-text);
865+
font-size: 13px;
866+
cursor: pointer;
867+
flex-shrink: 0;
868+
}
869+
870+
.schedules-add button:hover {
871+
background: var(--accent-hover);
872+
}
873+
874+
.schedules-empty {
875+
padding: 32px 16px;
876+
text-align: center;
877+
color: var(--text-muted);
878+
font-size: 13px;
879+
}
880+
881+
.schedule-error {
882+
padding: 8px 16px;
883+
color: var(--status-err-text);
884+
background: var(--status-err-bg);
885+
font-size: 12px;
886+
border-radius: 6px;
887+
margin: 8px 16px;
888+
}
889+
890+
/* --- Skeleton loading --- */
891+
892+
.skeleton-block {
893+
display: block;
894+
height: 14px;
895+
border-radius: 4px;
896+
background: linear-gradient(
897+
90deg,
898+
var(--bg-tertiary) 25%,
899+
var(--bg-secondary) 50%,
900+
var(--bg-tertiary) 75%
901+
);
902+
background-size: 200% 100%;
903+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
904+
}
905+
906+
.skeleton-timing {
907+
width: 72px;
908+
flex-shrink: 0;
909+
}
910+
911+
.skeleton-prompt {
912+
flex: 1;
913+
}
914+
915+
@keyframes skeleton-shimmer {
916+
0% { background-position: 200% 0; }
917+
100% { background-position: -200% 0; }
918+
}
919+
676920
/* --- Domains panel --- */
677921

678922
.domains-list {

0 commit comments

Comments
 (0)