ostacky 0.8.2 → 0.8.4
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 +20 -25
- package/assets/agents/ostacky.md +18 -3
- package/assets/commands/install-stack.md +2 -2
- package/assets/mcp/ostacky-controller/controller-core.js +206 -0
- package/assets/mcp/ostacky-controller/index.js +166 -164
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/controller-core.ts +206 -0
- package/assets/plugins/ostacky-plugin.ts +12 -124
- package/assets/skills/brainstorming/SKILL.md +5 -3
- package/dist/cli.js +503 -720
- package/manifest.json +43 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,16 +110,13 @@ Detecta automáticamente el directorio `.opencode/` del proyecto (o lo crea) y m
|
|
|
110
110
|
### Instalar todo
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
|
-
npx ostacky install # local
|
|
114
|
-
npx ostacky install --scope local # <proyecto>/.opencode
|
|
115
|
-
npx ostacky install --scope global # ~/.config/opencode (XDG/APPDATA en Windows)
|
|
116
|
-
npx ostacky install --scope auto # local si existe .opencode/.git, si no global
|
|
113
|
+
npx ostacky install # siempre local en <proyecto>/.opencode
|
|
114
|
+
npx ostacky install --scope local # <proyecto>/.opencode (único scope soportado)
|
|
117
115
|
```
|
|
118
116
|
|
|
119
|
-
Descarga todos los agentes y commands definidos en el manifest y los escribe en `.
|
|
117
|
+
Descarga todos los agentes y commands definidos en el manifest y los escribe en `<proyecto>/.opencode`. Además copia los plugins **Ostacky-owned** `ostacky-plugin.ts` + `engram.ts` en `.opencode/plugins/` (el legacy `ostacky-guard.ts` fue fusionado en `ostacky-plugin.ts` y ya no se instala; `ostacky-controller.ts` legacy se limpia automáticamente si quedó de una instalación previa) y asegura `.gitignore` con ` .opencode/tools/, .codegraph/, .opencode/cache/, ostacky-state.json*`.
|
|
120
118
|
|
|
121
|
-
> **
|
|
122
|
-
> **Windows — dónde se instala realmente:** `global` es `%APPDATA%\opencode` (`C:\Users\<vos>\AppData\Roaming\opencode`), **no** `C:\Users\<vos>\.opencode`. Si ves `C:\Users\...\ .opencode\tools\codegraph` y esperabas `D:\mi-proyecto\.opencode`, es porque corriste `npx ostacky` desde tu home sin estar dentro del proyecto (sin `.git`). Hacé `cd` al proyecto y re-ejecutá con `--scope local`; el log ahora muestra `projectRoot` y `toolsDir` explícitos para evitar confusión.
|
|
119
|
+
> **Instalación siempre local:** Ostacky vive por proyecto/worktree (CodeGraph index `.codegraph/` y `ostacky-state.json` aislados). Si ves `C:\Users\...\ .opencode\tools\codegraph` y esperabas `D:\mi-proyecto\.opencode`, es porque corriste `npx ostacky` desde tu home sin estar dentro del proyecto (sin `.git`). Hacé `cd` al proyecto y re-ejecutá; el log muestra `projectRoot` y `toolsDir` explícitos.
|
|
123
120
|
|
|
124
121
|
### Agregar agentes o commands individualmente
|
|
125
122
|
|
|
@@ -151,10 +148,8 @@ Solo descarga los items que cambiaron de versión.
|
|
|
151
148
|
### Desinstalar
|
|
152
149
|
|
|
153
150
|
```bash
|
|
154
|
-
npx ostacky uninstall #
|
|
151
|
+
npx ostacky uninstall # qué borrar en <proyecto>/.opencode
|
|
155
152
|
npx ostacky uninstall --scope local # fuerza scope local
|
|
156
|
-
npx ostacky uninstall --scope global # fuerza scope global
|
|
157
|
-
npx ostacky uninstall --scope auto # auto
|
|
158
153
|
```
|
|
159
154
|
|
|
160
155
|
**Safe-delete:** solo borra lo trackeado en `.opencode/ostacky-lock.json` (agentes, commands, skills, MCPs) — nunca toca componentes previos del usuario que no estén en el lockfile. Antes de borrar muestra preview con `Scope: <ruta>` + lista de paths y pide confirmación. Los plugins Ostacky-owned (`ostacky-plugin.ts`, `engram.ts` + legacy `ostacky-guard.ts`/`ostacky-controller.ts` si quedaron) se limpian automáticamente via allowlist, nunca se borran plugins custom.
|
|
@@ -164,7 +159,7 @@ npx ostacky uninstall --scope auto # auto
|
|
|
164
159
|
#### Desinstalar un agente puntual
|
|
165
160
|
|
|
166
161
|
```bash
|
|
167
|
-
npx ostacky uninstall agent <nombre> # también admite --scope local
|
|
162
|
+
npx ostacky uninstall agent <nombre> # también admite --scope local
|
|
168
163
|
npx ostacky uninstall agent # sin nombre → selector de instalados
|
|
169
164
|
```
|
|
170
165
|
|
|
@@ -177,7 +172,7 @@ npx ostacky uninstall agent ostacky
|
|
|
177
172
|
#### Desinstalar un command puntual
|
|
178
173
|
|
|
179
174
|
```bash
|
|
180
|
-
npx ostacky uninstall command <nombre> # también admite --scope
|
|
175
|
+
npx ostacky uninstall command <nombre> # también admite --scope local
|
|
181
176
|
npx ostacky uninstall command # sin nombre → selector
|
|
182
177
|
```
|
|
183
178
|
|
|
@@ -192,7 +187,7 @@ Si no especificás el nombre, el CLI te muestra un selector con los items instal
|
|
|
192
187
|
#### Desinstalar todo el stack (tools + config)
|
|
193
188
|
|
|
194
189
|
```bash
|
|
195
|
-
npx ostacky uninstall-stack #
|
|
190
|
+
npx ostacky uninstall-stack # limpia mcp.codegraph/engram de opencode.json + .codegraph/ + .opencode/tools/
|
|
196
191
|
npx ostacky uninstall-stack --scope local
|
|
197
192
|
```
|
|
198
193
|
|
|
@@ -251,37 +246,37 @@ Tras instalar (`--scope local`), el proyecto queda así:
|
|
|
251
246
|
|
|
252
247
|
```json
|
|
253
248
|
{
|
|
254
|
-
"version": "0.8.
|
|
249
|
+
"version": "0.8.4",
|
|
255
250
|
"lockedAt": "2025-01-01T00:00:00.000Z",
|
|
256
251
|
"repo": "JaimeHoracio/Ostacky",
|
|
257
|
-
"tag": "v0.8.
|
|
252
|
+
"tag": "v0.8.4",
|
|
258
253
|
"agents": {
|
|
259
254
|
"ostacky": {
|
|
260
|
-
"version": "0.8.
|
|
255
|
+
"version": "0.8.4",
|
|
261
256
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
262
257
|
"sha256": "abc123..."
|
|
263
258
|
}
|
|
264
259
|
},
|
|
265
260
|
"commands": {
|
|
266
261
|
"install-stack": {
|
|
267
|
-
"version": "0.8.
|
|
262
|
+
"version": "0.8.4",
|
|
268
263
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
269
264
|
"sha256": "def456..."
|
|
270
265
|
},
|
|
271
266
|
"opsx-sync": {
|
|
272
|
-
"version": "0.8.
|
|
267
|
+
"version": "0.8.4",
|
|
273
268
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
274
269
|
"sha256": "ghi789..."
|
|
275
270
|
}
|
|
276
271
|
},
|
|
277
272
|
"skills": {
|
|
278
|
-
"brainstorming": { "version": "0.8.
|
|
279
|
-
"execution-mode-evaluation": { "version": "0.8.
|
|
280
|
-
"openspec-propose": { "version": "0.8.
|
|
273
|
+
"brainstorming": { "version": "0.8.4", ... },
|
|
274
|
+
"execution-mode-evaluation": { "version": "0.8.4", ... },
|
|
275
|
+
"openspec-propose": { "version": "0.8.4", ... }
|
|
281
276
|
},
|
|
282
277
|
"mcpServers": {
|
|
283
|
-
"ostacky-controller": { "version": "0.8.
|
|
284
|
-
"openspec": { "version": "0.8.
|
|
278
|
+
"ostacky-controller": { "version": "0.8.4", ... },
|
|
279
|
+
"openspec": { "version": "0.8.4", ... }
|
|
285
280
|
}
|
|
286
281
|
}
|
|
287
282
|
```
|
|
@@ -313,7 +308,7 @@ Es opcional y solo necesario si algo falló durante la instalación o si querés
|
|
|
313
308
|
## Seguridad
|
|
314
309
|
|
|
315
310
|
- `opencode.jsonc` se versiona en el repo para compartir permisos y MCP de forma reproducible.
|
|
316
|
-
- Las URLs de descarga usan **tags de GitHub** (ej. `v0.8.
|
|
311
|
+
- Las URLs de descarga usan **tags de GitHub** (ej. `v0.8.4`), nunca `main` — instalaciones reproducibles
|
|
317
312
|
- Cada path de archivo descargado es validado para prevenir **path traversal**
|
|
318
313
|
- Los archivos incluyen **checksum SHA-256** opcional; si el manifest lo define, el contenido se verifica antes de escribir
|
|
319
314
|
- El cache local (`.opencode/cache/`) también valida integridad al servir archivos cacheados
|
|
@@ -370,7 +365,7 @@ CodeGraph está instalado en `.opencode/tools/codegraph/bin/codegraph` y se conf
|
|
|
370
365
|
| `.opencode/tools/codegraph/bin/codegraph status` | Muestra estado del index y archivos pendientes |
|
|
371
366
|
| `.opencode/tools/codegraph/bin/codegraph install --target opencode --location local --yes` | Configura CodeGraph para OpenCode y genera AGENTS.md |
|
|
372
367
|
|
|
373
|
-
> **Windows:** si ves `Command failed: cmd.exe /d /c call ... codegraph.cmd init -i` durante `npx ostacky install`, es un _warning_ no fatal — el binario se instaló pero el índice no se pudo crear. Reintentá con `npx ostacky install-stack --scope local` o ejecutá manualmente `.opencode\tools\codegraph\bin\codegraph.exe init -i` (o `codegraph.cmd` si no hay `.exe`) dentro del proyecto. Desde v0.8.
|
|
368
|
+
> **Windows:** si ves `Command failed: cmd.exe /d /c call ... codegraph.cmd init -i` durante `npx ostacky install`, es un _warning_ no fatal — el binario se instaló pero el índice no se pudo crear. Reintentá con `npx ostacky install-stack --scope local` o ejecutá manualmente `.opencode\tools\codegraph\bin\codegraph.exe init -i` (o `codegraph.cmd` si no hay `.exe`) dentro del proyecto. Desde v0.8.4 el instalador usa el patrón resiliente de Engram (strip 0 + búsqueda recursiva) y loguea `projectRoot|toolsDir` para diagnosticar scope.
|
|
374
369
|
|
|
375
370
|
## Licencia
|
|
376
371
|
|
package/assets/agents/ostacky.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Orquestador principal — rutea por nivel, orquesta CodeGraph + OpenSpec + Superpowers.
|
|
3
3
|
mode: primary
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.4
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Sos **Ostacky v0.8.
|
|
7
|
+
Sos **Ostacky v0.8.4**, orquestás, no implementás. Interpretás, clasificás (0/0+1/1+), ruteás y coordinás.
|
|
8
8
|
|
|
9
|
-
> **Versión:** `0.8.
|
|
9
|
+
> **Versión:** `0.8.4` (sincronizada desde `package.json` vía `scripts/sync-version.ts`). Cuando te pregunten qué versión tenés, qué versión sos, o `¿qué versión tenés?` / `version` / `¿en qué versión estás?`, respondé exactamente: **"Ostacky v0.8.4"** (o `v0.8.4` si te piden solo el número). No inventes otra versión.
|
|
10
10
|
|
|
11
11
|
## Reglas innegociables
|
|
12
12
|
|
|
@@ -18,6 +18,16 @@ Sos **Ostacky v0.8.2**, orquestás, no implementás. Interpretás, clasificás (
|
|
|
18
18
|
|
|
19
19
|
> Ver `assets/docs/ostacky-reference.md` para TRANSITIONS, TTL y métricas. Tiered LITE/TIER1/FULL vía suffix hint.
|
|
20
20
|
|
|
21
|
+
## Principios de honestidad (SHALL)
|
|
22
|
+
|
|
23
|
+
1. **Engram contradiction check SHALL.** Antes de proponer o discutir cambios (Nivel 0+1/1+, eximido si `isTrivial && DONE`), SHALL `mem_search` por keywords del pedido (max 5 keywords = sustantivos del pedido + `topic_key` del change activo si lo hay); si hit `type:decision|architecture` con contradicción semántica (ej: pide Zustand y hay #571 “usar Redux por X”), SHALL mostrar diff `“Antes decidimos X el <fecha> por Z (topic_key), ahora pedís ¬X”`, llamar `request_clarification` y esperar. Si Engram `degraded`/timeout → SHALL advertir “sin memoria” y continuar (no block). Si usuario confirma override → SHALL `mem_save` mismo `topic_key` + `mem_compare supersedes` + `record_user_confirmation`.
|
|
24
|
+
2. **Solo propuestas que ayudan SHALL.** Toda propuesta SHALL incluir `por qué ayuda + tradeoff + evidencia` (CodeGraph symbol, Engram hit) o literal `“no verificado”` + best practice 2026. No sugerir por sugerir.
|
|
25
|
+
3. **Pocas honestas > muchas de relleno SHALL.** Si solo hay una opción honesta, dar una. Si hay 2-3, tabla `coste|riesgo|complejidad`. No inventar para llenar.
|
|
26
|
+
4. **Si no hay propuesta honesta SHALL decirlo.** Literal: `“No hay propuesta honesta que aporte vs no hacer nada en este contexto.”`
|
|
27
|
+
5. **Si dudás SHALL preguntar.** Ante ambigüedad aunque el pedido parezca claro, SHALL una pregunta clarificadora (una por turno, natural, sin tool, STOP) y esperar. No asumir.
|
|
28
|
+
6. **Verificar antes de afirmar SHALL.** No acordar sin chequear código/docs. Citar evidencia o admitir `“no pude verificar en disco”`.
|
|
29
|
+
7. **YAGNI scope SHALL.** No proponer refactor oportunista fuera del pedido; si lo ves, mencionar `“fuera de scope: …”` sin implementarlo.
|
|
30
|
+
|
|
21
31
|
## Stack
|
|
22
32
|
|
|
23
33
|
- **Controller** (plugin `ostacky-plugin.ts`): state machine in-process, hard gates.
|
|
@@ -62,6 +72,11 @@ Preguntar nivel y `consume_route_decision`.
|
|
|
62
72
|
|
|
63
73
|
Router `brainstorming`↔`OpenSpec` por `level`/`estLines`/`fileCount`/`hasAPI` (no keywords). `1+` no-downgradeable → `skill(brainstorming)` genera `design.md ## Alternatives`; downgradeable → `docs/...` + `DIRECT`.
|
|
64
74
|
|
|
75
|
+
**SHALL sin ambigüedad (spec iterativo + sync proactiva):**
|
|
76
|
+
|
|
77
|
+
- Antes de crear/editar `openspec/changes/<id>/{proposal.md,design.md,tasks.md}` **O** `docs/superpowers/specs/*.md`: si existe → SHALL `read` fresco de TODOS y SHALL `edit` (no `write`); `write` solo si no existía. Para `open-explore` sin archivo → SHALL `mem_save topic_key:brainstorm/<hash>` por iteración. `getDiscoverySnapshot` no aplica a specs/docs.
|
|
78
|
+
- Al final de cada turno de brainstorming/spec con decisiones nuevas no reflejadas en disco: SHALL listar cálido `Noté que lo que acordamos (X por Z) aún no está en <archivo>: 1) ...` (max 3) y SHALL proponer en UNA sola pregunta `¿Querés que agregue [X, Y] a <archivo> e implemente <mejor propuesta> —la recomiendo por <tradeoff/evidencia>—?` (respetando Regla 5). Si no hay delta, no proponer. Eximido si `isTrivial`.
|
|
79
|
+
|
|
65
80
|
### 4. Execution
|
|
66
81
|
|
|
67
82
|
1. `skill(execution-mode-evaluation)` en memoria, reusa discovery.
|
|
@@ -5,13 +5,13 @@ agent: build
|
|
|
5
5
|
|
|
6
6
|
Instala el stack tecnológico de desarrollo para OpenCode. **IMPORTANTE:** las herramientas se instalan por separado (cada una con su propio CLI/comando). `npx ostacky install` solo instala el agente y commands de Ostacky en `.opencode/`. Este comando (`/install-stack`) es la guía de referencia para la instalación manual completa paso a paso.
|
|
7
7
|
|
|
8
|
-
**Nota:** A partir de v0.8.
|
|
8
|
+
**Nota:** A partir de v0.8.4, `npx ostacky install` ya instala automáticamente el stack completo (CodeGraph, OpenSpec, Engram, MCPs bundleados) además del agente y skills. Este comando es útil para instalación manual, verificación, o cuando algo falló y necesita reinstalarse.
|
|
9
9
|
|
|
10
10
|
**RESTRICCIÓN ABSOLUTA:** instalar ÚNICAMENTE para OpenCode. Está terminantemente prohibido crear o modificar archivos en `.claude/`, `.kiro/`, `.cursor/`, `.gemini/`, `.codex/`, `.antigravity/`, `.windsurf/` o cualquier otro directorio de plataformas externas.
|
|
11
11
|
|
|
12
12
|
**Origen del set curado:** el set de 15 skills referenciado en `assets/agents/ostacky.md` está bundleado en `assets/skills/` dentro del paquete npm. La definición del set y su trazabilidad viven en `manifest.json` y `.opencode/ostacky-lock.json`.
|
|
13
13
|
|
|
14
|
-
## Scope de instalación — local vs global (desde v0.8.
|
|
14
|
+
## Scope de instalación — local vs global (desde v0.8.4)
|
|
15
15
|
|
|
16
16
|
`npx ostacky install` soporta `--scope local|global|auto` (también `--scope=...`):
|
|
17
17
|
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* controller-core — Single source of truth para TRANSITIONS, STATES, DEFAULT_STATE y helpers.
|
|
3
|
+
* Extraído de assets/mcp/ostacky-controller/index.js y assets/plugins/ostacky-plugin.ts
|
|
4
|
+
* para eliminar duplicación (D1). Ambos importan de acá.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { SENSITIVE_DEFAULT } from "./security.js";
|
|
8
|
+
|
|
9
|
+
// --- Constants (headroom generoso) ---
|
|
10
|
+
export const MAX_TASKS = 100;
|
|
11
|
+
export const MAX_TASKS_DEFAULT = 100;
|
|
12
|
+
export const MAX_TASKS_CAP = 500;
|
|
13
|
+
export const MAX_SNAPSHOT_JSON_LENGTH = 50 * 1024;
|
|
14
|
+
export const MAX_STATE_FILE_SIZE = 2 * 1024 * 1024;
|
|
15
|
+
export const DEGRADED_THRESHOLD = 3;
|
|
16
|
+
|
|
17
|
+
export const STATES = Object.freeze({
|
|
18
|
+
INTERPRETATION_PENDING: "INTERPRETATION_PENDING",
|
|
19
|
+
CLARIFICATION_PENDING: "CLARIFICATION_PENDING",
|
|
20
|
+
DISCOVERY: "DISCOVERY",
|
|
21
|
+
ROUTE_DECISION_PENDING: "ROUTE_DECISION_PENDING",
|
|
22
|
+
SPECIFICATION: "SPECIFICATION",
|
|
23
|
+
EXECUTION_ANALYSIS: "EXECUTION_ANALYSIS",
|
|
24
|
+
EXECUTION_DECISION_PENDING: "EXECUTION_DECISION_PENDING",
|
|
25
|
+
EXECUTING_INLINE: "EXECUTING_INLINE",
|
|
26
|
+
EXECUTING_SUBAGENTS: "EXECUTING_SUBAGENTS",
|
|
27
|
+
SYNC: "SYNC",
|
|
28
|
+
DONE: "DONE",
|
|
29
|
+
BLOCKED: "BLOCKED",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const TRANSITIONS = {
|
|
33
|
+
INTERPRETATION_PENDING: [
|
|
34
|
+
{ via: "request_clarification", to: "CLARIFICATION_PENDING" },
|
|
35
|
+
{ via: "proceed_to_discovery", to: "DISCOVERY" },
|
|
36
|
+
{ via: "record_discovery", to: "ROUTE_DECISION_PENDING" },
|
|
37
|
+
{ via: "block", to: "BLOCKED" },
|
|
38
|
+
],
|
|
39
|
+
CLARIFICATION_PENDING: [
|
|
40
|
+
{ via: "record_clarification", to: "DISCOVERY" },
|
|
41
|
+
{ via: "block", to: "BLOCKED" },
|
|
42
|
+
{ via: "abandon", to: "BLOCKED" },
|
|
43
|
+
],
|
|
44
|
+
DISCOVERY: [
|
|
45
|
+
{ via: "record_discovery", to: "ROUTE_DECISION_PENDING" },
|
|
46
|
+
{ via: "block", to: "BLOCKED" },
|
|
47
|
+
{ via: "abandon", to: "BLOCKED" },
|
|
48
|
+
],
|
|
49
|
+
ROUTE_DECISION_PENDING: [
|
|
50
|
+
{ via: "consume_route_decision", to: "SPECIFICATION", choice: "SPEC" },
|
|
51
|
+
{ via: "consume_route_decision", to: "EXECUTION_ANALYSIS", choice: "DIRECT" },
|
|
52
|
+
{ via: "block", to: "BLOCKED" },
|
|
53
|
+
{ via: "abandon", to: "BLOCKED" },
|
|
54
|
+
],
|
|
55
|
+
SPECIFICATION: [
|
|
56
|
+
{ via: "spec_complete", to: "EXECUTION_ANALYSIS" },
|
|
57
|
+
{ via: "block", to: "BLOCKED" },
|
|
58
|
+
{ via: "abandon", to: "BLOCKED" },
|
|
59
|
+
],
|
|
60
|
+
EXECUTION_ANALYSIS: [
|
|
61
|
+
{ via: "record_execution_analysis", to: "EXECUTION_DECISION_PENDING" },
|
|
62
|
+
{ via: "block", to: "BLOCKED" },
|
|
63
|
+
{ via: "abandon", to: "BLOCKED" },
|
|
64
|
+
],
|
|
65
|
+
EXECUTION_DECISION_PENDING: [
|
|
66
|
+
{ via: "consume_execution_decision", to: "EXECUTING_INLINE", mode: "INLINE" },
|
|
67
|
+
{ via: "consume_execution_decision", to: "EXECUTING_SUBAGENTS", mode: "SUBAGENT_DRIVEN" },
|
|
68
|
+
{ via: "block", to: "BLOCKED" },
|
|
69
|
+
{ via: "abandon", to: "BLOCKED" },
|
|
70
|
+
],
|
|
71
|
+
EXECUTING_INLINE: [
|
|
72
|
+
{ via: "implementation_complete", to: "SYNC" },
|
|
73
|
+
{ via: "block", to: "BLOCKED" },
|
|
74
|
+
],
|
|
75
|
+
EXECUTING_SUBAGENTS: [
|
|
76
|
+
{ via: "implementation_complete", to: "SYNC" },
|
|
77
|
+
{ via: "block", to: "BLOCKED" },
|
|
78
|
+
],
|
|
79
|
+
BLOCKED: [
|
|
80
|
+
{ via: "replan", to: "INTERPRETATION_PENDING" },
|
|
81
|
+
{ via: "abandon", to: "DONE" },
|
|
82
|
+
],
|
|
83
|
+
SYNC: [
|
|
84
|
+
{ via: "sync_complete", to: "DONE" },
|
|
85
|
+
{ via: "block", to: "BLOCKED" },
|
|
86
|
+
],
|
|
87
|
+
DONE: [],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const TERMINAL_STATES = Object.freeze([
|
|
91
|
+
STATES.INTERPRETATION_PENDING,
|
|
92
|
+
STATES.CLARIFICATION_PENDING,
|
|
93
|
+
STATES.BLOCKED,
|
|
94
|
+
STATES.DONE,
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
export const DEFAULT_STATE = {
|
|
98
|
+
state: STATES.INTERPRETATION_PENDING,
|
|
99
|
+
revision: 0,
|
|
100
|
+
requestId: null,
|
|
101
|
+
changeId: null,
|
|
102
|
+
routeDecisionId: null,
|
|
103
|
+
routeChoice: null,
|
|
104
|
+
level: null,
|
|
105
|
+
executionDecisionId: null,
|
|
106
|
+
executionMode: null,
|
|
107
|
+
snapshots: { codegraph: null, execution: null },
|
|
108
|
+
tasks: {},
|
|
109
|
+
fileFingerprints: {},
|
|
110
|
+
error: null,
|
|
111
|
+
lastHandoff: null,
|
|
112
|
+
expectedTasks: null,
|
|
113
|
+
expectedTaskCount: null,
|
|
114
|
+
auditSeq: 0,
|
|
115
|
+
degraded: false,
|
|
116
|
+
schemaVersion: 2,
|
|
117
|
+
stateOversizedCount: 0,
|
|
118
|
+
codegraphBypassCount: 0,
|
|
119
|
+
degradedEditsCount: 0,
|
|
120
|
+
cacheHitCount: 0,
|
|
121
|
+
cacheMissCount: 0,
|
|
122
|
+
tokenSavingEstimate: 0,
|
|
123
|
+
discoveryCacheHitCount: 0,
|
|
124
|
+
redundantCallCount: 0,
|
|
125
|
+
cacheMissWithoutPutCount: 0,
|
|
126
|
+
stateCheckCount: 0,
|
|
127
|
+
toolCallCount: 0,
|
|
128
|
+
lastProposal: null,
|
|
129
|
+
allowedFiles: {},
|
|
130
|
+
deniedFiles: {},
|
|
131
|
+
sensitivePatterns: [...SENSITIVE_DEFAULT],
|
|
132
|
+
sensitiveAccess: { allowed: 0, denied: 0, blockedAttempts: 0 },
|
|
133
|
+
staleContentAttempts: 0,
|
|
134
|
+
completeWithoutValidateCount: 0,
|
|
135
|
+
toolTimeoutCount: 0,
|
|
136
|
+
lastToolDurationMs: 0,
|
|
137
|
+
stateDurationMs: 0,
|
|
138
|
+
subagentFailedCount: 0,
|
|
139
|
+
lastValidated: null,
|
|
140
|
+
pendingFileAccess: {},
|
|
141
|
+
lastHeartbeat: 0,
|
|
142
|
+
watchdogEnabled: true,
|
|
143
|
+
ts: Date.now(),
|
|
144
|
+
specNotInSync: false,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export function safeJsonStringify(obj, pretty = false) {
|
|
148
|
+
const seen = new WeakSet();
|
|
149
|
+
try {
|
|
150
|
+
return JSON.stringify(
|
|
151
|
+
obj,
|
|
152
|
+
(key, value) => {
|
|
153
|
+
if (typeof value === "object" && value !== null) {
|
|
154
|
+
if (seen.has(value)) return "[Circular]";
|
|
155
|
+
seen.add(value);
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
},
|
|
159
|
+
pretty ? 2 : undefined
|
|
160
|
+
);
|
|
161
|
+
} catch (e) {
|
|
162
|
+
return `[Unstringifiable: ${e.message}]`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const SENSITIVE_REDACT_RE = /(apiKey|secret|token|password|api_key)/i;
|
|
167
|
+
|
|
168
|
+
export function redactForLog(data) {
|
|
169
|
+
if (!data || typeof data !== "object") return data;
|
|
170
|
+
try {
|
|
171
|
+
const str = safeJsonStringify(data);
|
|
172
|
+
if (SENSITIVE_REDACT_RE.test(str)) {
|
|
173
|
+
const copy = JSON.parse(str);
|
|
174
|
+
const redactRecursively = (obj) => {
|
|
175
|
+
if (!obj || typeof obj !== "object") return;
|
|
176
|
+
for (const k of Object.keys(obj)) {
|
|
177
|
+
if (SENSITIVE_REDACT_RE.test(k)) obj[k] = "[REDACTED]";
|
|
178
|
+
else if (typeof obj[k] === "object") redactRecursively(obj[k]);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
redactRecursively(copy);
|
|
182
|
+
return copy;
|
|
183
|
+
}
|
|
184
|
+
return data;
|
|
185
|
+
} catch {
|
|
186
|
+
return data;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function getMaxTasks() {
|
|
191
|
+
const raw = process.env.OSTACKY_MAX_TASKS;
|
|
192
|
+
if (raw == null || raw === "") return MAX_TASKS_DEFAULT;
|
|
193
|
+
const n = parseInt(raw, 10);
|
|
194
|
+
if (Number.isNaN(n) || n <= 0) return MAX_TASKS_DEFAULT;
|
|
195
|
+
if (n > MAX_TASKS_CAP) return MAX_TASKS_CAP;
|
|
196
|
+
return n;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function getAuditRetention() {
|
|
200
|
+
const raw = process.env.OSTACKY_AUDIT_RETENTION;
|
|
201
|
+
if (raw == null || raw === "") return 500;
|
|
202
|
+
const n = parseInt(raw, 10);
|
|
203
|
+
if (Number.isNaN(n) || n <= 0) return 500;
|
|
204
|
+
if (n > 2000) return 2000;
|
|
205
|
+
return n;
|
|
206
|
+
}
|