paperclip-plugin-autopilot-ops 0.0.0-stage → 0.1.0

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/README.md CHANGED
@@ -1,3 +1,107 @@
1
- # Temporary Holding Version
1
+ # Autopilot Care · plugin `autopilot-ops` para Paperclip
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Operación autónoma gobernada para instituciones de salud. Este plugin es la capa
4
+ determinística que corre **dentro** de Paperclip (mismo proceso de plugins, misma
5
+ base, misma UI): workflows por puesto, validaciones de negocio, registro de cada
6
+ acción con su evidencia, políticas de autonomía con circuito de corte y el tablero
7
+ del director. Los agentes de Paperclip no tocan nada directo: deciden y llaman
8
+ herramientas de este plugin; el tool gateway de Paperclip autoriza; el plugin
9
+ ejecuta y registra.
10
+
11
+ Arquitectura completa: documento *Arquitectura Autopilot Care sobre Paperclip*.
12
+
13
+ ## Qué hay adentro
14
+
15
+ | Capa | Dónde | Qué hace |
16
+ | --- | --- | --- |
17
+ | Workflows | `src/workflows.ts` | Estados, tabla de transiciones, quién puede ejecutar cada una, validaciones puras. Sin IA. |
18
+ | Registro de negocio | `migrations/001_autopilot_ops.sql`, `src/ops.ts` | Tablas `caso`, `transicion`, `accion`, `politica`, `politica_historial` en el namespace del plugin. Todo lleva `company_id`. |
19
+ | Herramientas para agentes | `src/worker.ts` (`ctx.tools`) | `ops_listar_casos`, `ops_leer_caso`, `ops_abrir_caso`, `ops_transicionar`, `ops_registrar_accion`, `ops_consultar_indicador`. Pasan por el tool gateway. |
20
+ | Gobierno | `politica` + UI | Modo por workflow e institución: `corte` · `observa` · `propone` · `ejecuta`. Default `propone`. |
21
+ | Tablero del director | `src/ui/index.tsx` | Pantalla `/<empresa>/autopilot`: horas absorbidas, pendientes de aprobación con Aprobar / Rechazar, estado por puesto, políticas y corte. |
22
+ | API para el portal | `apiRoutes` (auth `board`) | `GET /tablero`, `GET /casos`, `GET /casos/:id`, `POST /casos/:id/aprobar`, `POST /casos/:id/rechazar`, `GET /politicas`, `POST /politicas`. Montadas en `/api/plugins/alephoo.autopilot-ops/api/*`. |
23
+
24
+ ### Workflows incluidos
25
+
26
+ **`actualizacion_arancel`** (puesto Convenios y facturación, 25 min absorbidos por caso)
27
+
28
+ ```
29
+ detectado ─proponer(agente)→ pendiente_aprobacion ─aprobar(humano)→ aprobado ─aplicar(agente, modo ejecuta)→ aplicado ─verificar→ verificado
30
+ └rechazar(humano)→ rechazado └→ con_diferencias ─proponer→ …
31
+ ```
32
+
33
+ Validaciones de `proponer`: valor nuevo > 0, vigencia `AAAA-MM-DD`, fuente
34
+ obligatoria, variación > 50 % requiere justificación. `aplicar` exige el id de la
35
+ escritura registrada con `ops_registrar_accion`. `verificar` compara el valor
36
+ releído con el aprobado y decide el destino.
37
+
38
+ **`tarea_operativa`** (genérico, 15 min): `abierta → en_curso → pendiente_aprobacion → cerrada | rechazada`.
39
+
40
+ ### Reglas que el plugin impone siempre
41
+
42
+ - `aprobar`, `rechazar`, `cancelar` y cambiar la política son **solo humanas**.
43
+ - `companyId` viene del host (contexto de ejecución del agente, bridge de la UI o
44
+ ruta API). Un agente no puede ver ni tocar casos de otra institución.
45
+ - Un agente solo registra **escrituras** sobre el sistema del hospital si el
46
+ workflow está en modo `ejecuta`.
47
+ - En `corte` el agente no abre ni mueve casos.
48
+ - Si una validación falla, el caso no cambia y el agente recibe el motivo con un
49
+ código estable (`validacion: fuente_presente: …`).
50
+ - Toda transición y acción queda en el registro; además se escribe en el
51
+ activity log de Paperclip.
52
+ - `idempotencyKey` en `ops_abrir_caso` y `ops_transicionar` hace los reintentos
53
+ seguros; `referencia` evita abrir dos casos sobre el mismo objeto.
54
+
55
+ ## Desarrollo
56
+
57
+ ```bash
58
+ pnpm install
59
+ pnpm verify # typecheck + tests + build
60
+ pnpm dev # esbuild en modo watch (dist/manifest.js, dist/worker.js, dist/ui/)
61
+ ```
62
+
63
+ Tests (`tests/`): motor de workflows, cableado de herramientas / acciones / rutas
64
+ con el harness del SDK, y `sql.spec.ts`, que pasa la migración y todo el SQL de
65
+ ejecución por una copia del validador de SQL del propio host Paperclip.
66
+
67
+ ### Agregar un workflow
68
+
69
+ 1. Definirlo en `src/workflows.ts` (estados, `inicial`, finales, `minutosAbsorbidos`, transiciones con `actor`, `nivelRequerido` y `validar`).
70
+ 2. Agregarlo a `WORKFLOWS`. No hace falta tocar la base: los casos son genéricos.
71
+ 3. Los tests de `tests/workflows.spec.ts` verifican invariantes para todos los workflows.
72
+
73
+ ## Instalación en una instancia Paperclip
74
+
75
+ Paperclip instala plugins de dos formas (`POST /api/plugins/install`, solo
76
+ administradores de instancia):
77
+
78
+ 1. **Desde npm** (la pantalla *Settings → Plugins → Install Plugin* solo acepta
79
+ un nombre de paquete npm): la instancia corre `npm install <paquete>` en su
80
+ directorio de plugins. Requiere publicar este paquete como
81
+ `paperclip-plugin-autopilot-ops`:
82
+
83
+ ```bash
84
+ pnpm install && pnpm verify # deja dist/ construido; la instalación corre con --ignore-scripts
85
+ npm login
86
+ npm publish
87
+ ```
88
+
89
+ Después, en la pantalla, escribir `paperclip-plugin-autopilot-ops` e Install.
90
+ 2. **Desde una ruta local del servidor**: clonar este repositorio en la máquina
91
+ donde corre Paperclip, `pnpm install && pnpm build`, y luego
92
+ `paperclipai plugin install /ruta/al/plugin` (o la misma pantalla con la
93
+ opción de ruta local). En desarrollo, `pnpm dev` deja el worker recargando.
94
+
95
+ Después de instalar, el agente que va a usar las herramientas necesita un
96
+ **perfil de herramientas** que permita las de este plugin (Agent settings →
97
+ Tools); el gateway niega por defecto todo lo que no está permitido.
98
+
99
+ ## Primer caso de uso (MVP)
100
+
101
+ Agente `Turnos`/`Convenios` detecta un cambio de arancel en el sistema de
102
+ gestión → `ops_abrir_caso` (`actualizacion_arancel`, referencia = convenio +
103
+ práctica) → `ops_registrar_accion` (lectura, evidencia) → `ops_transicionar`
104
+ `proponer` con `valor_nuevo`, `vigencia_desde`, `fuente` → el responsable del
105
+ hospital ve el caso en *Pendientes de aprobación* y aprueba → en modo `ejecuta`,
106
+ el agente aplica, registra la escritura y verifica → el tablero suma 25 minutos
107
+ absorbidos.
@@ -0,0 +1,232 @@
1
+ // src/manifest.ts
2
+ var PLUGIN_ID = "alephoo.autopilot-ops";
3
+ var PLUGIN_VERSION = "0.1.0";
4
+ var DB_NAMESPACE_SLUG = "autopilot_ops";
5
+ var PAGE_ROUTE = "autopilot";
6
+ var TOOL_NAMES = {
7
+ listarCasos: "ops_listar_casos",
8
+ leerCaso: "ops_leer_caso",
9
+ abrirCaso: "ops_abrir_caso",
10
+ transicionar: "ops_transicionar",
11
+ registrarAccion: "ops_registrar_accion",
12
+ consultarIndicador: "ops_consultar_indicador"
13
+ };
14
+ var DATA_KEYS = {
15
+ tablero: "tablero",
16
+ casos: "casos",
17
+ caso: "caso",
18
+ politicas: "politicas",
19
+ workflows: "workflows"
20
+ };
21
+ var ACTION_KEYS = {
22
+ aprobar: "aprobar",
23
+ rechazar: "rechazar",
24
+ cambiarPolitica: "cambiar-politica",
25
+ abrirCaso: "abrir-caso"
26
+ };
27
+ var manifest = {
28
+ id: PLUGIN_ID,
29
+ apiVersion: 1,
30
+ version: PLUGIN_VERSION,
31
+ displayName: "Autopilot Care",
32
+ description: "Operaci\xF3n aut\xF3noma gobernada para instituciones de salud: workflows determin\xEDsticos por puesto, validaciones, registro de negocio con evidencia, pol\xEDticas (observa / propone / ejecuta / corte) y tablero del director.",
33
+ author: "Alephoo",
34
+ categories: ["automation", "ui"],
35
+ capabilities: [
36
+ "database.namespace.migrate",
37
+ "database.namespace.read",
38
+ "database.namespace.write",
39
+ "agent.tools.register",
40
+ "api.routes.register",
41
+ "activity.log.write",
42
+ "companies.read",
43
+ "ui.sidebar.register",
44
+ "ui.page.register"
45
+ ],
46
+ entrypoints: {
47
+ worker: "./dist/worker.js",
48
+ ui: "./dist/ui"
49
+ },
50
+ database: {
51
+ namespaceSlug: DB_NAMESPACE_SLUG,
52
+ migrationsDir: "migrations",
53
+ coreReadTables: ["companies", "agents", "issues"]
54
+ },
55
+ tools: [
56
+ {
57
+ name: TOOL_NAMES.listarCasos,
58
+ displayName: "Listar casos",
59
+ description: "Lista casos del registro de negocio de la instituci\xF3n. Filtros opcionales: workflow, estado, limit (m\xE1x. 100). Solo lectura.",
60
+ parametersSchema: {
61
+ type: "object",
62
+ properties: {
63
+ workflow: { type: "string" },
64
+ estado: { type: "string" },
65
+ limit: { type: "number" }
66
+ }
67
+ }
68
+ },
69
+ {
70
+ name: TOOL_NAMES.leerCaso,
71
+ displayName: "Leer caso",
72
+ description: "Devuelve un caso con su historial de transiciones y acciones registradas. Solo lectura.",
73
+ parametersSchema: {
74
+ type: "object",
75
+ properties: { casoId: { type: "string" } },
76
+ required: ["casoId"]
77
+ }
78
+ },
79
+ {
80
+ name: TOOL_NAMES.abrirCaso,
81
+ displayName: "Abrir caso",
82
+ description: "Abre un caso nuevo en un workflow, en su estado inicial. `referencia` identifica el objeto real (convenio, cuenta, profesional) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese. Pasar `idempotencyKey` para reintentos seguros.",
83
+ parametersSchema: {
84
+ type: "object",
85
+ properties: {
86
+ workflow: { type: "string" },
87
+ referencia: { type: "string" },
88
+ titulo: { type: "string" },
89
+ datos: { type: "object" },
90
+ idempotencyKey: { type: "string" }
91
+ },
92
+ required: ["workflow", "titulo"]
93
+ }
94
+ },
95
+ {
96
+ name: TOOL_NAMES.transicionar,
97
+ displayName: "Transicionar caso",
98
+ description: "Aplica una transici\xF3n del workflow al caso (p. ej. `proponer`, `aplicar`, `verificar`). El plugin valida contra la tabla de transiciones, la pol\xEDtica vigente (observa / propone / ejecuta / corte) y las reglas de negocio; si algo falla, el caso no cambia y se devuelve el motivo. Las transiciones humanas (aprobar, rechazar) no est\xE1n disponibles para agentes.",
99
+ parametersSchema: {
100
+ type: "object",
101
+ properties: {
102
+ casoId: { type: "string" },
103
+ via: { type: "string" },
104
+ datos: { type: "object" },
105
+ motivo: { type: "string" },
106
+ idempotencyKey: { type: "string" }
107
+ },
108
+ required: ["casoId", "via"]
109
+ }
110
+ },
111
+ {
112
+ name: TOOL_NAMES.registrarAccion,
113
+ displayName: "Registrar acci\xF3n",
114
+ description: "Registra una acci\xF3n ejecutada sobre un sistema externo (lectura, escritura o verificaci\xF3n) con su evidencia. Toda acci\xF3n sobre el sistema del hospital debe quedar registrada ac\xE1.",
115
+ parametersSchema: {
116
+ type: "object",
117
+ properties: {
118
+ casoId: { type: "string" },
119
+ tipo: { type: "string", enum: ["lectura", "escritura", "verificacion"] },
120
+ conector: { type: "string", enum: ["api", "archivo", "pantalla", "manual"] },
121
+ objetivo: { type: "string" },
122
+ resultado: { type: "string", enum: ["ok", "error", "pendiente"] },
123
+ evidencia: { type: "object" },
124
+ detalle: { type: "string" }
125
+ },
126
+ required: ["casoId", "tipo", "conector", "objetivo", "resultado"]
127
+ }
128
+ },
129
+ {
130
+ name: TOOL_NAMES.consultarIndicador,
131
+ displayName: "Consultar indicador",
132
+ description: "Devuelve indicadores calculados del registro de negocio (los mismos que ve el director): `resumen`, `por_estado`, `pendientes`, `horas`. Solo lectura.",
133
+ parametersSchema: {
134
+ type: "object",
135
+ properties: {
136
+ indicador: { type: "string", enum: ["resumen", "por_estado", "pendientes", "horas"] },
137
+ workflow: { type: "string" },
138
+ dias: { type: "number" }
139
+ },
140
+ required: ["indicador"]
141
+ }
142
+ }
143
+ ],
144
+ apiRoutes: [
145
+ {
146
+ routeKey: "tablero",
147
+ method: "GET",
148
+ path: "/tablero",
149
+ auth: "board",
150
+ capability: "api.routes.register",
151
+ companyResolution: { from: "query", key: "companyId" }
152
+ },
153
+ {
154
+ routeKey: "casos",
155
+ method: "GET",
156
+ path: "/casos",
157
+ auth: "board",
158
+ capability: "api.routes.register",
159
+ companyResolution: { from: "query", key: "companyId" }
160
+ },
161
+ {
162
+ routeKey: "caso",
163
+ method: "GET",
164
+ path: "/casos/:casoId",
165
+ auth: "board",
166
+ capability: "api.routes.register",
167
+ companyResolution: { from: "query", key: "companyId" }
168
+ },
169
+ {
170
+ routeKey: "aprobar",
171
+ method: "POST",
172
+ path: "/casos/:casoId/aprobar",
173
+ auth: "board",
174
+ capability: "api.routes.register",
175
+ companyResolution: { from: "body", key: "companyId" }
176
+ },
177
+ {
178
+ routeKey: "rechazar",
179
+ method: "POST",
180
+ path: "/casos/:casoId/rechazar",
181
+ auth: "board",
182
+ capability: "api.routes.register",
183
+ companyResolution: { from: "body", key: "companyId" }
184
+ },
185
+ {
186
+ routeKey: "politicas",
187
+ method: "GET",
188
+ path: "/politicas",
189
+ auth: "board",
190
+ capability: "api.routes.register",
191
+ companyResolution: { from: "query", key: "companyId" }
192
+ },
193
+ {
194
+ routeKey: "cambiar-politica",
195
+ method: "POST",
196
+ path: "/politicas",
197
+ auth: "board",
198
+ capability: "api.routes.register",
199
+ companyResolution: { from: "body", key: "companyId" }
200
+ }
201
+ ],
202
+ ui: {
203
+ slots: [
204
+ {
205
+ type: "sidebar",
206
+ id: "autopilot-sidebar",
207
+ displayName: "Autopilot Care",
208
+ exportName: "SidebarLink",
209
+ order: 30
210
+ },
211
+ {
212
+ type: "page",
213
+ id: "autopilot-page",
214
+ displayName: "Autopilot Care",
215
+ exportName: "TableroPage",
216
+ routePath: PAGE_ROUTE
217
+ }
218
+ ]
219
+ }
220
+ };
221
+ var manifest_default = manifest;
222
+ export {
223
+ ACTION_KEYS,
224
+ DATA_KEYS,
225
+ DB_NAMESPACE_SLUG,
226
+ PAGE_ROUTE,
227
+ PLUGIN_ID,
228
+ PLUGIN_VERSION,
229
+ TOOL_NAMES,
230
+ manifest_default as default
231
+ };
232
+ //# sourceMappingURL=manifest.js.map