Add Context Manager client and log schema

- context_manager.py: Full stateless client for Anthropic API
  - Builds context from cto.blocks + cto.memory + log.messages
  - Logs all messages to immutable log.messages table
  - Interactive chat mode with CLI commands

- context_bridge.py: PostgreSQL bridge for context queries
  - Peer auth support for local connections
  - get_all_relevant_blocks() single query optimization
  - build_system_prompt() from blocks

- 04_log.sql: Immutable log schema
  - log.messages with hash chain integrity
  - Triggers preventing UPDATE/DELETE

- 07_initial_blocks.sql: Initial context blocks
  - tzzr_base, rules_base, r2_storage
  - server_architect, server_deck, server_corp

- cm: Launcher script with venv support
This commit is contained in:
ARCHITECT
2026-01-01 22:41:20 +00:00
parent 05d21976ca
commit d1b2c16bd0
5 changed files with 728 additions and 107 deletions

View File

@@ -1,119 +1,60 @@
-- ============================================
-- SCHEMA LOG - Sistema TZZR
-- Log inmutable de mensajes
-- Versión: 2.0
-- Fecha: 2026-01-01
-- ============================================
-- Schema LOG para Context Manager
CREATE SCHEMA IF NOT EXISTS log;
DROP SCHEMA IF EXISTS log CASCADE;
CREATE SCHEMA log;
-- Extensiones
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Tipos
CREATE TYPE log.ref_type AS ENUM ('context', 'accountant', 'secretary');
-- ============================================
-- Tabla principal: messages
-- ============================================
CREATE TABLE log.messages (
id BIGSERIAL PRIMARY KEY,
hash CHAR(64) UNIQUE NOT NULL,
session_hash CHAR(64) NOT NULL,
thread_hash CHAR(64),
owner_id CHAR(64) NOT NULL,
players_id CHAR(64)[] NOT NULL DEFAULT '{}',
master_player CHAR(64),
role TEXT,
content TEXT NOT NULL,
attachments JSONB DEFAULT '{}',
prev_hash CHAR(64),
hashtags CHAR(64)[] DEFAULT '{}',
flag_id CHAR(64),
master_item_id CHAR(64),
item_id CHAR(64)[] DEFAULT '{}',
loc_id CHAR(64),
ambient JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
CREATE TABLE IF NOT EXISTS log.messages (
id BIGSERIAL PRIMARY KEY,
hash CHAR(64) UNIQUE NOT NULL,
session_hash CHAR(64) NOT NULL,
thread_hash CHAR(64),
owner_id CHAR(64),
players_id CHAR(64)[],
master_player CHAR(64),
role TEXT NOT NULL,
content TEXT NOT NULL,
attachments JSONB DEFAULT '{}',
prev_hash CHAR(64),
hashtags CHAR(64)[],
flag_id CHAR(64),
master_item_id CHAR(64),
item_id CHAR(64)[],
loc_id CHAR(64),
ambient JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Índices messages
CREATE INDEX idx_messages_session ON log.messages(session_hash);
CREATE INDEX idx_messages_thread ON log.messages(thread_hash);
CREATE INDEX idx_messages_owner ON log.messages(owner_id);
CREATE INDEX idx_messages_players ON log.messages USING gin(players_id);
CREATE INDEX idx_messages_master ON log.messages(master_player);
CREATE INDEX idx_messages_prev ON log.messages(prev_hash);
CREATE INDEX idx_messages_hashtags ON log.messages USING gin(hashtags);
CREATE INDEX idx_messages_flag ON log.messages(flag_id);
CREATE INDEX idx_messages_master_item ON log.messages(master_item_id);
CREATE INDEX idx_messages_items ON log.messages USING gin(item_id);
CREATE INDEX idx_messages_loc ON log.messages(loc_id);
CREATE INDEX idx_messages_created ON log.messages(created_at);
-- ============================================
-- Tabla relacional: message_refs
-- Referencias a contexto y conocimiento
-- ============================================
CREATE TABLE log.message_refs (
id BIGSERIAL PRIMARY KEY,
message_hash CHAR(64) NOT NULL,
ref_hash CHAR(64) NOT NULL,
ref_type log.ref_type NOT NULL,
position INT NOT NULL,
thread_hash CHAR(64),
UNIQUE(message_hash, ref_hash, ref_type)
CREATE TABLE IF NOT EXISTS log.message_refs (
id BIGSERIAL PRIMARY KEY,
message_hash CHAR(64) NOT NULL,
ref_hash CHAR(64) NOT NULL,
ref_type VARCHAR(20) NOT NULL,
position INT,
thread_hash CHAR(64)
);
-- Índices message_refs
CREATE INDEX idx_refs_message ON log.message_refs(message_hash);
CREATE INDEX idx_refs_ref ON log.message_refs(ref_hash);
CREATE INDEX idx_refs_type ON log.message_refs(ref_type);
CREATE INDEX idx_refs_thread ON log.message_refs(thread_hash);
CREATE INDEX IF NOT EXISTS idx_log_session ON log.messages(session_hash);
CREATE INDEX IF NOT EXISTS idx_log_thread ON log.messages(thread_hash);
CREATE INDEX IF NOT EXISTS idx_log_owner ON log.messages(owner_id);
CREATE INDEX IF NOT EXISTS idx_log_prev ON log.messages(prev_hash);
CREATE INDEX IF NOT EXISTS idx_log_created ON log.messages(created_at);
CREATE INDEX IF NOT EXISTS idx_refs_message ON log.message_refs(message_hash);
CREATE INDEX IF NOT EXISTS idx_refs_ref ON log.message_refs(ref_hash);
-- ============================================
-- Funciones
-- ============================================
CREATE OR REPLACE FUNCTION log.sha256(data TEXT) RETURNS CHAR(64) AS $$
CREATE OR REPLACE FUNCTION log.prevent_modification() RETURNS TRIGGER AS $$
BEGIN
RETURN encode(digest(data, 'sha256'), 'hex');
END;
$$ LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION log.prevent_update() RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'UPDATE no permitido en %', TG_TABLE_NAME;
RAISE EXCEPTION 'Log is immutable';
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION log.prevent_delete() RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'DELETE no permitido en %', TG_TABLE_NAME;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS no_update_messages ON log.messages;
DROP TRIGGER IF EXISTS no_delete_messages ON log.messages;
CREATE TRIGGER no_update_messages BEFORE UPDATE ON log.messages FOR EACH ROW EXECUTE FUNCTION log.prevent_modification();
CREATE TRIGGER no_delete_messages BEFORE DELETE ON log.messages FOR EACH ROW EXECUTE FUNCTION log.prevent_modification();
-- ============================================
-- Triggers de protección (inmutabilidad)
-- ============================================
CREATE TRIGGER protect_messages_update BEFORE UPDATE ON log.messages
FOR EACH ROW EXECUTE FUNCTION log.prevent_update();
CREATE TRIGGER protect_messages_delete BEFORE DELETE ON log.messages
FOR EACH ROW EXECUTE FUNCTION log.prevent_delete();
DROP TRIGGER IF EXISTS no_update_refs ON log.message_refs;
DROP TRIGGER IF EXISTS no_delete_refs ON log.message_refs;
CREATE TRIGGER no_update_refs BEFORE UPDATE ON log.message_refs FOR EACH ROW EXECUTE FUNCTION log.prevent_modification();
CREATE TRIGGER no_delete_refs BEFORE DELETE ON log.message_refs FOR EACH ROW EXECUTE FUNCTION log.prevent_modification();
CREATE TRIGGER protect_refs_update BEFORE UPDATE ON log.message_refs
FOR EACH ROW EXECUTE FUNCTION log.prevent_update();
CREATE TRIGGER protect_refs_delete BEFORE DELETE ON log.message_refs
FOR EACH ROW EXECUTE FUNCTION log.prevent_delete();
-- ============================================
-- Permisos
-- ============================================
GRANT USAGE ON SCHEMA log TO tzzr;
GRANT SELECT, INSERT ON log.messages TO tzzr;
GRANT SELECT, INSERT ON log.message_refs TO tzzr;
GRANT USAGE ON SEQUENCE log.messages_id_seq TO tzzr;
GRANT USAGE ON SEQUENCE log.message_refs_id_seq TO tzzr;
-- Verificación
SELECT 'Schema log v2.0: 2 tablas creadas' as status;
GRANT USAGE ON SCHEMA log TO architect;
GRANT SELECT, INSERT ON ALL TABLES IN SCHEMA log TO architect;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA log TO architect;

View File

@@ -0,0 +1,79 @@
-- Bloques iniciales para Context Manager
-- Permitir NULL en session_hash y owner_id si no está ya
ALTER TABLE cto.blocks ALTER COLUMN session_hash DROP NOT NULL;
ALTER TABLE cto.blocks ALTER COLUMN owner_id DROP NOT NULL;
-- BLOQUE GLOBAL: Info base TZZR
INSERT INTO cto.blocks (hash, code, name, category, content, priority, scope, status)
VALUES (encode(sha256('tzzr_base'::bytea), 'hex'), 'tzzr_base', 'TZZR Base', 'system',
'# Sistema TZZR
## Servidores
| Servidor | IP | Propósito |
|----------|-----|-----------|
| ARCHITECT | 69.62.126.110 | Desarrollo |
| DECK | 72.62.1.113 | Usuario |
| CORP | 92.112.181.188 | Corporativo |
## Acceso SSH
Usar clave ~/.ssh/tzzr para conexiones entre servidores.', 100, 'global', 'active')
ON CONFLICT (code) DO NOTHING;
-- BLOQUE GLOBAL: Reglas de operación
INSERT INTO cto.blocks (hash, code, name, category, content, priority, scope, status)
VALUES (encode(sha256('rules_base'::bytea), 'hex'), 'rules_base', 'Reglas Base', 'rules',
'## Reglas de Operación
- Ejecutar comandos sin pedir confirmación
- No guardar documentos en servidor local, usar R2
- Limpieza automática: subir a R2 y eliminar archivos temporales', 90, 'global', 'active')
ON CONFLICT (code) DO NOTHING;
-- BLOQUE GLOBAL: R2 Storage
INSERT INTO cto.blocks (hash, code, name, category, content, priority, scope, status)
VALUES (encode(sha256('r2_storage'::bytea), 'hex'), 'r2_storage', 'R2 Storage', 'knowledge',
'## Almacenamiento R2
Endpoint: https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com
### Bucket: architect
- documentos adjuntos/ - Documentos para usuario
- system/ - Configs y backups
- auditorias/ - Logs', 80, 'global', 'active')
ON CONFLICT (code) DO NOTHING;
-- BLOQUE SERVER: ARCHITECT
INSERT INTO cto.blocks (hash, code, name, category, content, priority, scope, server_code, status)
VALUES (encode(sha256('server_architect'::bytea), 'hex'), 'server_architect', 'Servidor ARCHITECT', 'config',
'## ARCHITECT (69.62.126.110)
Servidor de desarrollo.
Servicios locales:
- Gitea: http://localhost:3000
- PostgreSQL: localhost:5432
SSH a otros servidores:
- ssh -i ~/.ssh/tzzr root@72.62.1.113 (DECK)
- ssh -i ~/.ssh/tzzr root@92.112.181.188 (CORP)', 70, 'server', 'ARCHITECT', 'active')
ON CONFLICT (code) DO NOTHING;
-- BLOQUE SERVER: DECK
INSERT INTO cto.blocks (hash, code, name, category, content, priority, scope, server_code, status)
VALUES (encode(sha256('server_deck'::bytea), 'hex'), 'server_deck', 'Servidor DECK', 'config',
'## DECK (72.62.1.113)
Servidor de usuario.', 70, 'server', 'DECK', 'active')
ON CONFLICT (code) DO NOTHING;
-- BLOQUE SERVER: CORP
INSERT INTO cto.blocks (hash, code, name, category, content, priority, scope, server_code, status)
VALUES (encode(sha256('server_corp'::bytea), 'hex'), 'server_corp', 'Servidor CORP', 'config',
'## CORP (92.112.181.188)
Servidor corporativo.', 70, 'server', 'CORP', 'active')
ON CONFLICT (code) DO NOTHING;
SELECT code, category, scope, server_code, priority FROM cto.blocks ORDER BY priority DESC;