diff --git a/CHANGELOG.md b/CHANGELOG.md index e9112a3..b7f8b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,32 @@ All notable changes to Void 2.0 are documented here. Format: [Keep a Changelog](https://keepachangelog.com). +## [2.0.0-alpha.6] — 2026-06-01 + +### Changed (Plan 5b: companion backend → Claude CLI subprocess) + +- **Companion model backend switched from the Anthropic API to the `claude` + CLI subprocess**, authenticated by the owner's **Claude Max subscription** + (no API key — the Agent SDK can't use subscription auth headlessly, and Max + doesn't issue API keys). Mirrors Void 1.0's `lib/agent.js`: spawn `claude` + with `ANTHROPIC_API_KEY`/`ANTHROPIC_AUTH_TOKEN` stripped so it uses the + logged-in subscription. The CLI owns the agentic loop; the four companion + tools are exposed to it via a local **stdio MCP server** (`lib/mcp/`). +- `lib/ai/claude_cli.js` — spawns `claude --print --output-format stream-json + --include-partial-messages --append-system-prompt … (--session-id | --resume) + --mcp-config … --strict-mcp-config --tools … --allowedTools …`, maps stream-json + → `{delta,tool,tool_result,result,error}`. Prompt fed via **stdin** (variadic + `--tools` would eat a positional). Multi-turn continuity via `--resume`. +- `lib/mcp/companion-stdio.js` — stdio MCP server re-exposing `companionRegistry`; + per-turn Space/agent context passed via env in the `--mcp-config`. +- `propose_change` now stamps the current Space onto created space-scoped + entities (model can't know the Space uuid). +- CT 311 runs the `claude` CLI (logged in as `void`, `HOME=/var/lib/void`). +- Built-in CLI tools (Bash/Read/Write/…) disabled via `--tools`; the companion + has only the four `mcp__void__*` tools. +- The old `@anthropic-ai/sdk` API-key path (`lib/ai/anthropic.js`, `runTurn`) + is retained in-tree but no longer the companion's execution path. + ## [2.0.0-alpha.5] — 2026-06-01 ### Added (Plan 5: Companion chat) diff --git a/docs/superpowers/plans/2026-06-01-void-v2-plan5b-claude-cli-pivot.md b/docs/superpowers/plans/2026-06-01-void-v2-plan5b-claude-cli-pivot.md new file mode 100644 index 0000000..64d4b04 --- /dev/null +++ b/docs/superpowers/plans/2026-06-01-void-v2-plan5b-claude-cli-pivot.md @@ -0,0 +1,118 @@ +# Plan 5b — Companion model backend: Claude CLI subprocess (Max subscription) + +> Amendment to Plan 5. Replaces the Anthropic-API-key model backend with the +> `claude` CLI subprocess approach (subscription auth), mirroring Void 1.0's +> `lib/agent.js`. REQUIRED SUB-SKILL for execution: subagent-driven-development. + +**Why:** Claude Max has no API key; the Agent SDK can't use subscription auth headlessly (ToS). Void 1.0 already powers Claude by spawning the locally-authenticated `claude` CLI (it strips `ANTHROPIC_API_KEY` from the child env so the CLI uses the Max subscription). We replicate that. The CLI owns the agentic loop; our four tools are exposed to it via a local MCP server. + +**Unchanged from Plan 5:** the tool defs + `companionRegistry` (T5–T9), migration 007 + `findOrCreateForSpace` (T3–T4), the per-Space conversation model, persistence, the right-rail UI (T13–T14), and the SSE event vocabulary the UI consumes (`delta` / `tool` / `draft` / `error` / `done`). + +**Removed/replaced:** `lib/ai/anthropic.js` (API-key adapter) and `lib/ai/agent/runtime.js` (`runTurn`) are no longer the execution path. Keep the files for now but the companion route stops importing them. `@anthropic-ai/sdk` dependency stays (harmless) or is removed in cleanup. + +## Architecture + +``` +Browser rail ──POST /turn (SSE)──▶ void-server companion route + │ writes per-turn: system-prompt file + mcp-config (env carries space/agent/view) + ▼ + spawn `claude -p --output-format stream-json --include-partial-messages + --session-id --append-system-prompt + --mcp-config --strict-mcp-config + --allowedTools mcp__void__search,mcp__void__read,mcp__void__context,mcp__void__propose_change + ` + (child env: ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN deleted → Max subscription auth) + │ stdout = stream-json lines + ▼ + map events → SSE (text_delta→delta, tool_use_start→tool, tool_result→(detect draft), result→done) + │ claude calls tools ──stdio MCP──▶ lib/mcp/companion-stdio.js + ▼ (reuses companionRegistry handlers + pool; + persist assistant message + draft_ids reads SPACE_ID/AGENT_ID/VOID_VIEW from env) +``` + +**MCP transport decision:** stdio. The route writes a per-turn `--mcp-config` JSON declaring one server `void` = `{command:"node", args:["/opt/void-server/lib/mcp/companion-stdio.js"], env:{SPACE_ID, AGENT_ID, VOID_VIEW_JSON, DATABASE_URL,…}}`. claude spawns it; it serves the 4 tools over stdio and runs the same handlers against the DB. No new HTTP attack surface; context flows via env. `--strict-mcp-config` ensures only our server is used; built-in tools are excluded by not allow-listing them. + +**Draft detection:** `propose_change`'s MCP result already contains `pending_change_id`. The route detects a `tool_result` for `propose_change` (or reads the structured result) and emits a `draft` SSE event + collects the id for the assistant message metadata. (Alternatively the tool returns the id in `structuredContent`; the route maps it.) + +--- + +## Task B1: MCP server exposing the four tools (stdio) + +**Files:** Create `lib/mcp/companion-stdio.js`; Create `lib/mcp/context.js` (builds tool `ctx` from env); Test `tests/mcp/companion_tools.test.js` + +- [ ] **Step 1: Failing test** — import the registry-backed dispatch and assert each of the 4 tools is exposed with the right name + that calling `propose_change` through the MCP dispatch writes a `pending_changes` row (reuse the existing handler). Use a small exported `dispatch(toolName, args, ctx)` so the test doesn't need a live stdio transport. + +```javascript +// tests/mcp/companion_tools.test.js (sketch — implementer fills real assertions) +import { describe, it, expect, beforeAll } from 'vitest'; +import { pool } from '../../lib/db/pool.js'; +import { resetDb } from '../helpers/db.js'; +import { migrateUp } from '../../lib/db/migrate.js'; +import { listMcpTools, callMcpTool } from '../../lib/mcp/companion-stdio.js'; + +let spaceId, agentId; +beforeAll(async () => { await resetDb(); await migrateUp(); + ({rows:[{id:spaceId}]} = await pool.query(`INSERT INTO spaces(slug,name) VALUES('s','S') RETURNING id`)); + ({rows:[{id:agentId}]} = await pool.query(`SELECT id FROM agents WHERE slug='companion'`)); }); + +it('exposes the four tools', () => { + expect(listMcpTools().map(t=>t.name).sort()).toEqual(['context','propose_change','read','search']); +}); +it('propose_change writes a pending_changes row via MCP dispatch', async () => { + const ctx = { agent:{kind:'agent',id:agentId,capabilities:{read:true,suggest:true,write:false},scopes:{}}, space_id:spaceId }; + const out = await callMcpTool('propose_change', {entity_type:'task',action:'create',payload:{space_id:spaceId,title:'X'}}, ctx); + expect(out.pending_change_id).toBeTruthy(); +}); +``` + +- [ ] **Step 2:** Run, confirm fail. +- [ ] **Step 3:** Implement using the MCP SDK (`@modelcontextprotocol/sdk`) stdio server. Register each tool from `companionRegistry.listTools()` (name, description, JSON-Schema `input_schema`). On a tool call, build `ctx` from env via `lib/mcp/context.js` (`{ agent: JSON.parse(env.VOID_AGENT_JSON), space_id: env.SPACE_ID, view: env.VOID_VIEW_JSON?… }`) and invoke the registry handler; return the result as MCP `content` (JSON-stringified) + `structuredContent`. Export thin `listMcpTools()` / `callMcpTool(name,args,ctx)` for the unit test. When run as `main`, start the stdio transport. Add `@modelcontextprotocol/sdk` to deps. +- [ ] **Step 4:** Run, confirm pass. +- [ ] **Step 5:** Commit. + +## Task B2: Claude subprocess driver + +**Files:** Create `lib/ai/claude_cli.js`; Test `tests/ai/claude_cli.test.js` + +- [ ] Implement `runClaudeTurn({ sessionId, systemPrompt, userText, mcpConfigPath, allowedTools, onEvent, claudeExe=process.env.CLAUDE_EXE||'claude', cwd })` that: + - spawns the CLI with `--print --output-format stream-json --include-partial-messages --session-id --append-system-prompt --mcp-config --strict-mcp-config --allowedTools ` (verify exact flag names against `claude --help` on CT 311; V1 used `--append-system-prompt-file` — confirm whether the file variant or inline is correct in 2.1.159); + - **deletes `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` from the child env** (forces subscription auth); + - parses stdout stream-json lines and calls `onEvent` with normalized events `{type:'delta',text}` | `{type:'tool',tool,status}` | `{type:'tool_result',name,result}` | `{type:'result',usage,cost}` | `{type:'error',message}`; + - returns `{ text, toolTrace, usage }` on close; handles non-zero exit + timeout. +- [ ] **TEST WITHOUT THE REAL CLI:** make `claudeExe` injectable and point the test at a fake script (`tests/fixtures/fake-claude.js`, `#!/usr/bin/env node`) that emits canned stream-json lines (a text block, a `tool_use` for `propose_change`, a `tool_result`, a `result`). Assert `onEvent` receives mapped events and the return shape is right. No subscription, no network. +- [ ] Commit. + +## Task B3: Rework companion route onto the CLI driver + +**Files:** Modify `lib/api/routes/companion.js`; Modify `tests/api/companion.test.js` + +- [ ] The `POST …/turn` handler: + - persists the user message (unchanged); + - resolves `{agent, convo}` (unchanged); + - writes a per-turn system prompt (the existing `SYSTEM` text + a note that `propose_change` drafts go to the owner's inbox) and a per-turn `--mcp-config` temp JSON declaring the `void` stdio server with env `{SPACE_ID, VOID_AGENT_JSON, VOID_VIEW_JSON, DATABASE_URL}` (+ whatever the pool needs); + - calls `runClaudeTurn({ sessionId: convo.id, ... , claudeExe: req.app.locals.claudeExe||'claude', onEvent: e => send(...) })`, mapping driver events → existing SSE event names the UI expects (`delta`/`tool`/`draft`/`done`/`error`); detect `propose_change` results → `draft` events + collect ids; + - persists ONE assistant message with `{tool_trace, draft_ids, usage}` (unchanged shape); + - cleans up temp files. +- [ ] **Integration test:** inject `req.app.locals.claudeExe` = the fake-claude fixture path (same approach as the old `app.locals.callModel`). Assert SSE emits tool/draft/delta/done, user+assistant rows persisted, a `pending_changes` row created (the fake triggers the real MCP `propose_change`? — if the fake can't run the MCP server, instead have the fake emit a `tool_result` for propose_change and have the route create/detect the draft from that; keep the assertion that assistant.metadata.draft_ids has length 1). Keep it network-free. +- [ ] Remove the now-unused imports of `runTurn`/`makeCallModel` from the route. Commit. + +## Task B4: UI event-name reconciliation (only if needed) + +**Files:** possibly Modify `public/components/rightrail.js` + +- [ ] Confirm the route still emits exactly `delta`/`tool`/`draft`/`error`/`done` with the same field names the rail reads. If B3 introduced any new event names (e.g. `tool_use_start` vs `tool`), reconcile in the rail (render a chip per tool event; accumulate deltas). Likely a no-op. Commit only if changed. + +## Task B5: CT 311 enablement + redeploy + smoke + +- [ ] Ensure `claude` CLI present on CT 311 (done — v2.1.159) and the user has run `claude login` (subscription). Verify `claude -p "hi"` works with API-key env unset. +- [ ] `npm install` the new `@modelcontextprotocol/sdk` dep is on CT 311 (push.sh runs `npm install`). +- [ ] Confirm `CLAUDE_EXE` resolves on CT 311 for the `void` systemd user (PATH); set `CLAUDE_EXE=/path/to/claude` in `/opt/void-server/.env` if the service PATH doesn't include the global npm bin. +- [ ] Snapshot CT 310+311; `TARGET=root@192.168.1.216 ./deploy/push.sh`; verify `/health`. +- [ ] **Live smoke:** open the rail in a Space → ask a question (expect streamed answer; tool chips if it searches) → "create a task to X" → inline draft card → approve → task exists + clears from Inbox. +- [ ] Update CHANGELOG + `docs/plan-5-complete.md` (note the CLI-subprocess backend) + memory. Bump to alpha-6 if the deployed alpha-5 behavior materially changed. + +## Open risks / verify-during-build +- Exact `claude` 2.1.159 flag spellings (`--append-system-prompt` vs `--append-system-prompt-file`; `--allowedTools` value format — space-separated list vs repeated). Verify against `claude --help` on CT 311 in B2. +- stream-json schema in 2.1.159 (event `type`s) — sample real output once logged-in to confirm the mapping (V1's `processEvent` is the reference and should be close). +- The `void` systemd service user must have a logged-in `claude` credential. `claude login` stores creds in the invoking user's home (`~/.claude`/keychain). The service runs as user `void`; the login must be done AS the `void` user (e.g. `su void -c "claude"` /login), not root. Flag this in B5. +- MCP stdio child inherits env from claude (which inherits from void-server's spawn) → DATABASE_URL/space context must be set on the claude spawn env so it propagates. diff --git a/package-lock.json b/package-lock.json index a0689c0..e247353 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "void-server", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "void-server", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.6", "dependencies": { "@anthropic-ai/sdk": "^0.40.1", "@modelcontextprotocol/sdk": "^1.29.0", diff --git a/package.json b/package.json index 68d50ee..0d849cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "void-server", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.6", "type": "module", "private": true, "scripts": { diff --git a/server.js b/server.js index 9077327..cb989d9 100644 --- a/server.js +++ b/server.js @@ -8,7 +8,7 @@ import { registerWorkers } from './lib/jobs/index.js'; import { router as ingestRouter } from './lib/api/routes/ingest.js'; import { startCron } from './lib/cron/index.js'; -const VERSION = '2.0.0-alpha.5'; +const VERSION = '2.0.0-alpha.6'; export function createApp() { const app = express(); diff --git a/tests/server.test.js b/tests/server.test.js index 17d274f..e08f122 100644 --- a/tests/server.test.js +++ b/tests/server.test.js @@ -17,7 +17,7 @@ describe('server', () => { const res = await request(app).get('/health'); expect(res.status).toBe(200); expect(res.body.db_ok).toBe(true); - expect(res.body.version).toBe('2.0.0-alpha.5'); + expect(res.body.version).toBe('2.0.0-alpha.6'); }); it('GET /api/spaces without token returns 401', async () => { diff --git a/workers/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..56301f9 Binary files /dev/null and b/workers/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_boss.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_boss.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..5808b6a Binary files /dev/null and b/workers/tests/__pycache__/test_boss.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_echo.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_echo.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..166b12c Binary files /dev/null and b/workers/tests/__pycache__/test_echo.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_image.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_image.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..36dd6a1 Binary files /dev/null and b/workers/tests/__pycache__/test_image.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_model.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_model.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..3cf8951 Binary files /dev/null and b/workers/tests/__pycache__/test_model.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_pdf.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_pdf.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..c6a3baf Binary files /dev/null and b/workers/tests/__pycache__/test_pdf.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_safe_fetch.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_safe_fetch.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..ffce1f2 Binary files /dev/null and b/workers/tests/__pycache__/test_safe_fetch.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_sourcedoc.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_sourcedoc.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..3c5b031 Binary files /dev/null and b/workers/tests/__pycache__/test_sourcedoc.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/tests/__pycache__/test_video.cpython-312-pytest-9.0.3.pyc b/workers/tests/__pycache__/test_video.cpython-312-pytest-9.0.3.pyc new file mode 100644 index 0000000..fd39ddd Binary files /dev/null and b/workers/tests/__pycache__/test_video.cpython-312-pytest-9.0.3.pyc differ diff --git a/workers/void_workers.egg-info/PKG-INFO b/workers/void_workers.egg-info/PKG-INFO new file mode 100644 index 0000000..d3a8b1a --- /dev/null +++ b/workers/void_workers.egg-info/PKG-INFO @@ -0,0 +1,21 @@ +Metadata-Version: 2.4 +Name: void-workers +Version: 0.1.0 +Requires-Python: >=3.12 +Requires-Dist: psycopg[binary,pool]>=3.2 +Requires-Dist: structlog>=24.1 +Provides-Extra: pdf +Requires-Dist: pdfplumber>=0.11; extra == "pdf" +Requires-Dist: pytesseract>=0.3.13; extra == "pdf" +Requires-Dist: pillow>=10.3; extra == "pdf" +Provides-Extra: image +Requires-Dist: pytesseract>=0.3.13; extra == "image" +Requires-Dist: pillow>=10.3; extra == "image" +Provides-Extra: video +Requires-Dist: yt-dlp>=2024.10.0; extra == "video" +Requires-Dist: faster-whisper>=1.0.3; extra == "video" +Provides-Extra: test +Requires-Dist: pytest>=8.0; extra == "test" +Requires-Dist: pytest-asyncio>=0.23; extra == "test" +Provides-Extra: all +Requires-Dist: void-workers[image,pdf,test,video]; extra == "all" diff --git a/workers/void_workers.egg-info/SOURCES.txt b/workers/void_workers.egg-info/SOURCES.txt new file mode 100644 index 0000000..0fa37a1 --- /dev/null +++ b/workers/void_workers.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +README.md +pyproject.toml +void_workers/__init__.py +void_workers/config.py +void_workers/log.py +void_workers.egg-info/PKG-INFO +void_workers.egg-info/SOURCES.txt +void_workers.egg-info/dependency_links.txt +void_workers.egg-info/requires.txt +void_workers.egg-info/top_level.txt \ No newline at end of file diff --git a/workers/void_workers.egg-info/dependency_links.txt b/workers/void_workers.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/workers/void_workers.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/workers/void_workers.egg-info/requires.txt b/workers/void_workers.egg-info/requires.txt new file mode 100644 index 0000000..654ca3f --- /dev/null +++ b/workers/void_workers.egg-info/requires.txt @@ -0,0 +1,22 @@ +psycopg[binary,pool]>=3.2 +structlog>=24.1 + +[all] +void-workers[image,pdf,test,video] + +[image] +pytesseract>=0.3.13 +pillow>=10.3 + +[pdf] +pdfplumber>=0.11 +pytesseract>=0.3.13 +pillow>=10.3 + +[test] +pytest>=8.0 +pytest-asyncio>=0.23 + +[video] +yt-dlp>=2024.10.0 +faster-whisper>=1.0.3 diff --git a/workers/void_workers.egg-info/top_level.txt b/workers/void_workers.egg-info/top_level.txt new file mode 100644 index 0000000..8ed1607 --- /dev/null +++ b/workers/void_workers.egg-info/top_level.txt @@ -0,0 +1 @@ +void_workers diff --git a/workers/void_workers/__pycache__/__init__.cpython-312.pyc b/workers/void_workers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f6ad124 Binary files /dev/null and b/workers/void_workers/__pycache__/__init__.cpython-312.pyc differ diff --git a/workers/void_workers/__pycache__/boss.cpython-312.pyc b/workers/void_workers/__pycache__/boss.cpython-312.pyc new file mode 100644 index 0000000..364e9c1 Binary files /dev/null and b/workers/void_workers/__pycache__/boss.cpython-312.pyc differ diff --git a/workers/void_workers/__pycache__/config.cpython-312.pyc b/workers/void_workers/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..f5125a9 Binary files /dev/null and b/workers/void_workers/__pycache__/config.cpython-312.pyc differ diff --git a/workers/void_workers/__pycache__/log.cpython-312.pyc b/workers/void_workers/__pycache__/log.cpython-312.pyc new file mode 100644 index 0000000..cdcabc5 Binary files /dev/null and b/workers/void_workers/__pycache__/log.cpython-312.pyc differ diff --git a/workers/void_workers/__pycache__/model.cpython-312.pyc b/workers/void_workers/__pycache__/model.cpython-312.pyc new file mode 100644 index 0000000..61d8a17 Binary files /dev/null and b/workers/void_workers/__pycache__/model.cpython-312.pyc differ diff --git a/workers/void_workers/__pycache__/repo.cpython-312.pyc b/workers/void_workers/__pycache__/repo.cpython-312.pyc new file mode 100644 index 0000000..4bc64a8 Binary files /dev/null and b/workers/void_workers/__pycache__/repo.cpython-312.pyc differ diff --git a/workers/void_workers/__pycache__/runner.cpython-312.pyc b/workers/void_workers/__pycache__/runner.cpython-312.pyc new file mode 100644 index 0000000..9b59c5e Binary files /dev/null and b/workers/void_workers/__pycache__/runner.cpython-312.pyc differ diff --git a/workers/void_workers/__pycache__/safe_fetch.cpython-312.pyc b/workers/void_workers/__pycache__/safe_fetch.cpython-312.pyc new file mode 100644 index 0000000..0938edf Binary files /dev/null and b/workers/void_workers/__pycache__/safe_fetch.cpython-312.pyc differ diff --git a/workers/void_workers/handlers/__pycache__/__init__.cpython-312.pyc b/workers/void_workers/handlers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c288857 Binary files /dev/null and b/workers/void_workers/handlers/__pycache__/__init__.cpython-312.pyc differ diff --git a/workers/void_workers/handlers/__pycache__/echo.cpython-312.pyc b/workers/void_workers/handlers/__pycache__/echo.cpython-312.pyc new file mode 100644 index 0000000..24674b1 Binary files /dev/null and b/workers/void_workers/handlers/__pycache__/echo.cpython-312.pyc differ diff --git a/workers/void_workers/handlers/__pycache__/image.cpython-312.pyc b/workers/void_workers/handlers/__pycache__/image.cpython-312.pyc new file mode 100644 index 0000000..136ae11 Binary files /dev/null and b/workers/void_workers/handlers/__pycache__/image.cpython-312.pyc differ diff --git a/workers/void_workers/handlers/__pycache__/pdf.cpython-312.pyc b/workers/void_workers/handlers/__pycache__/pdf.cpython-312.pyc new file mode 100644 index 0000000..16f9c93 Binary files /dev/null and b/workers/void_workers/handlers/__pycache__/pdf.cpython-312.pyc differ diff --git a/workers/void_workers/handlers/__pycache__/sourcedoc.cpython-312.pyc b/workers/void_workers/handlers/__pycache__/sourcedoc.cpython-312.pyc new file mode 100644 index 0000000..96d1617 Binary files /dev/null and b/workers/void_workers/handlers/__pycache__/sourcedoc.cpython-312.pyc differ diff --git a/workers/void_workers/handlers/__pycache__/video.cpython-312.pyc b/workers/void_workers/handlers/__pycache__/video.cpython-312.pyc new file mode 100644 index 0000000..cd7a7ef Binary files /dev/null and b/workers/void_workers/handlers/__pycache__/video.cpython-312.pyc differ