oficina-opencode 2.0.1 → 2.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.
Files changed (89) hide show
  1. package/README.md +164 -14
  2. package/dist/core/atomic.d.ts +85 -0
  3. package/dist/core/atomic.d.ts.map +1 -0
  4. package/dist/core/atomic.js +186 -0
  5. package/dist/core/atomic.js.map +1 -0
  6. package/dist/core/bus.d.ts +76 -56
  7. package/dist/core/bus.d.ts.map +1 -1
  8. package/dist/core/bus.js +141 -100
  9. package/dist/core/bus.js.map +1 -1
  10. package/dist/core/catalog.d.ts +27 -0
  11. package/dist/core/catalog.d.ts.map +1 -1
  12. package/dist/core/catalog.js +41 -6
  13. package/dist/core/catalog.js.map +1 -1
  14. package/dist/core/cost.d.ts +275 -0
  15. package/dist/core/cost.d.ts.map +1 -0
  16. package/dist/core/cost.js +724 -0
  17. package/dist/core/cost.js.map +1 -0
  18. package/dist/core/docsync.d.ts +124 -0
  19. package/dist/core/docsync.d.ts.map +1 -0
  20. package/dist/core/docsync.js +191 -0
  21. package/dist/core/docsync.js.map +1 -0
  22. package/dist/core/evidence.d.ts +314 -0
  23. package/dist/core/evidence.d.ts.map +1 -0
  24. package/dist/core/evidence.js +602 -0
  25. package/dist/core/evidence.js.map +1 -0
  26. package/dist/core/fsx.d.ts.map +1 -1
  27. package/dist/core/fsx.js +7 -1
  28. package/dist/core/fsx.js.map +1 -1
  29. package/dist/core/gates.d.ts +9 -2
  30. package/dist/core/gates.d.ts.map +1 -1
  31. package/dist/core/gates.js +19 -3
  32. package/dist/core/gates.js.map +1 -1
  33. package/dist/core/ids.d.ts +48 -0
  34. package/dist/core/ids.d.ts.map +1 -0
  35. package/dist/core/ids.js +87 -0
  36. package/dist/core/ids.js.map +1 -0
  37. package/dist/core/leases.d.ts +45 -0
  38. package/dist/core/leases.d.ts.map +1 -1
  39. package/dist/core/leases.js +85 -17
  40. package/dist/core/leases.js.map +1 -1
  41. package/dist/core/office.d.ts +72 -1
  42. package/dist/core/office.d.ts.map +1 -1
  43. package/dist/core/office.js +173 -6
  44. package/dist/core/office.js.map +1 -1
  45. package/dist/core/paths.d.ts +11 -1
  46. package/dist/core/paths.d.ts.map +1 -1
  47. package/dist/core/paths.js +28 -4
  48. package/dist/core/paths.js.map +1 -1
  49. package/dist/core/recovery.d.ts +87 -0
  50. package/dist/core/recovery.d.ts.map +1 -0
  51. package/dist/core/recovery.js +325 -0
  52. package/dist/core/recovery.js.map +1 -0
  53. package/dist/core/sandbox.d.ts +179 -0
  54. package/dist/core/sandbox.d.ts.map +1 -0
  55. package/dist/core/sandbox.js +546 -0
  56. package/dist/core/sandbox.js.map +1 -0
  57. package/dist/core/state.d.ts +7 -0
  58. package/dist/core/state.d.ts.map +1 -1
  59. package/dist/core/state.js +15 -1
  60. package/dist/core/state.js.map +1 -1
  61. package/dist/core/stores.d.ts +65 -0
  62. package/dist/core/stores.d.ts.map +1 -0
  63. package/dist/core/stores.js +115 -0
  64. package/dist/core/stores.js.map +1 -0
  65. package/dist/core/tripwire.d.ts +87 -0
  66. package/dist/core/tripwire.d.ts.map +1 -0
  67. package/dist/core/tripwire.js +203 -0
  68. package/dist/core/tripwire.js.map +1 -0
  69. package/dist/core/types.d.ts +33 -1
  70. package/dist/core/types.d.ts.map +1 -1
  71. package/dist/core/types.js +1 -0
  72. package/dist/core/types.js.map +1 -1
  73. package/dist/hooks/scopeGuard.d.ts +86 -13
  74. package/dist/hooks/scopeGuard.d.ts.map +1 -1
  75. package/dist/hooks/scopeGuard.js +296 -62
  76. package/dist/hooks/scopeGuard.js.map +1 -1
  77. package/dist/index.d.ts +10 -0
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +185 -2
  80. package/dist/index.js.map +1 -1
  81. package/dist/tools/index.d.ts.map +1 -1
  82. package/dist/tools/index.js +158 -0
  83. package/dist/tools/index.js.map +1 -1
  84. package/docs/COMPATIBILITY.md +98 -0
  85. package/docs/GARANTIAS.md +190 -0
  86. package/docs/PLAN-V2.1.md +144 -0
  87. package/docs/QUE-ES.md +15 -0
  88. package/docs/catalog-facts.json +27 -0
  89. package/package.json +6 -2
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Reconciliación: qué hacer cuando la corrida quedó a medias.
3
+ *
4
+ * ## Por qué existe
5
+ *
6
+ * La oficina escribe en disco, en varios archivos, sin transaccion. Eso
7
+ * significa que un proceso que muere en el momento equivocado deja el
8
+ * estado incoherente:
9
+ *
10
+ * - El agente escribió `handoff.md` pero murió antes de llamar a
11
+ * `oficina_submit`: la card sigue en `in_progress` con la entrega ya
12
+ * escrita en disco. Nadie la va a terminar.
13
+ * - La card quedó en `done` pero `gates.md` no se escribió: se cerró sin
14
+ * gate. Eso es exactamente lo que el invariante `gate-required-before-done`
15
+ * dice que no puede pasar, y si pasó, fue por un crash.
16
+ * - Un agente murió con el lease tomado: la card queda trabada hasta que
17
+ * el TTL vence (30 minutos por defecto).
18
+ * - Un agente terminó su card pero quedó `working` en el roster: ocupa
19
+ * presupuesto de agentes para siempre.
20
+ *
21
+ * ## Qué hace
22
+ *
23
+ * Compara **estado vs artefactos vs filesystem** y reporta las
24
+ * incoherencias. Algunas se reparan solas (un lease huerfano se libera); las
25
+ * que requieren una decision (una card `done` sin gate) se reportan para
26
+ * que el Integrador decida.
27
+ *
28
+ * ## Lo que NO hace
29
+ *
30
+ * No adivina. No "completa" una card a la que le falta evidencia: la marca
31
+ * como incompleta y deja que un humano o un agente la retome. Un sistema
32
+ * que se auto-repara inventando evidencia es peor que uno que dice "esto
33
+ * esta roto".
34
+ */
35
+ import type { OfficeState } from "./types.js";
36
+ import type { Office } from "./office.js";
37
+ export type RecoveryIssueKind = "card-in-progress-without-owner" | "review-without-artifacts" | "done-without-gate" | "orphaned-artifact" | "stale-lease" | "lease-without-owner" | "agent-still-working-on-done-card" | "blocked-by-unresolvable-dependency" | "review-without-evidence";
38
+ export type RecoverySeverity = "critical" | "warning";
39
+ export interface RecoveryIssue {
40
+ kind: RecoveryIssueKind;
41
+ severity: RecoverySeverity;
42
+ cardId?: string;
43
+ agentId?: string;
44
+ detail: string;
45
+ /** Accion sugerida. */
46
+ repair: string;
47
+ /** true si la reconciliacion la resolvio sola. */
48
+ repaired: boolean;
49
+ }
50
+ export interface RecoveryReport {
51
+ runId: string;
52
+ checkedAt: string;
53
+ issues: RecoveryIssue[];
54
+ /** Resumen legible para el board. */
55
+ summary: string;
56
+ /** healthy = no hay issues criticos sin reparar. */
57
+ healthy: boolean;
58
+ }
59
+ export interface ReconcileOptions {
60
+ /** Aplicar las reparaciones seguras (liberar leases huerfanos). */
61
+ autoRepair?: boolean;
62
+ /** Reloj inyectable para tests deterministas. */
63
+ clock?: () => number;
64
+ /** No escribir nada, solo reportar. */
65
+ dryRun?: boolean;
66
+ }
67
+ /**
68
+ * Reconcilia la corrida.
69
+ *
70
+ * Devuelve el reporte. Con `autoRepair` aplica las reparaciones que no
71
+ * requieren decision humana (liberar leases, liberar agentes que
72
+ * empezaron la card de otro).
73
+ */
74
+ export declare function reconcile(office: Office, options?: ReconcileOptions): Promise<RecoveryReport>;
75
+ /** Renderiza el reporte como markdown. */
76
+ export declare function renderRecoveryReport(issues: RecoveryIssue[]): string;
77
+ /**
78
+ * El estado que una card deberia tener, dado lo que hay en disco.
79
+ *
80
+ * Se usa para el caso "el agente escribio y murio": si el handoff esta
81
+ * escrito pero la card sigue en `in_progress`, la card puede pasar a
82
+ * `review` sin que el agente tenga que volver a trabajar.
83
+ */
84
+ export declare function inferCardStateFromDisk(office: Office, cardId: string): Promise<"incomplete" | "deliverable" | "unknown">;
85
+ /** Snapshot del estado para detectar cambios inesperados. */
86
+ export declare function stateFingerprint(state: OfficeState): string;
87
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../src/core/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAKzC,MAAM,MAAM,iBAAiB,GACzB,gCAAgC,GAChC,0BAA0B,GAC1B,mBAAmB,GACnB,mBAAmB,GACnB,aAAa,GACb,qBAAqB,GACrB,kCAAkC,GAClC,oCAAoC,GACpC,yBAAyB,CAAA;AAE7B,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,CAAA;AAErD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAA;IACvB,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;IACpB,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAYD;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC,CA4OzB;AAED,0CAA0C;AAC1C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAuBpE;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,aAAa,GAAG,SAAS,CAAC,CAOnD;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAO3D"}
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Reconciliación: qué hacer cuando la corrida quedó a medias.
3
+ *
4
+ * ## Por qué existe
5
+ *
6
+ * La oficina escribe en disco, en varios archivos, sin transaccion. Eso
7
+ * significa que un proceso que muere en el momento equivocado deja el
8
+ * estado incoherente:
9
+ *
10
+ * - El agente escribió `handoff.md` pero murió antes de llamar a
11
+ * `oficina_submit`: la card sigue en `in_progress` con la entrega ya
12
+ * escrita en disco. Nadie la va a terminar.
13
+ * - La card quedó en `done` pero `gates.md` no se escribió: se cerró sin
14
+ * gate. Eso es exactamente lo que el invariante `gate-required-before-done`
15
+ * dice que no puede pasar, y si pasó, fue por un crash.
16
+ * - Un agente murió con el lease tomado: la card queda trabada hasta que
17
+ * el TTL vence (30 minutos por defecto).
18
+ * - Un agente terminó su card pero quedó `working` en el roster: ocupa
19
+ * presupuesto de agentes para siempre.
20
+ *
21
+ * ## Qué hace
22
+ *
23
+ * Compara **estado vs artefactos vs filesystem** y reporta las
24
+ * incoherencias. Algunas se reparan solas (un lease huerfano se libera); las
25
+ * que requieren una decision (una card `done` sin gate) se reportan para
26
+ * que el Integrador decida.
27
+ *
28
+ * ## Lo que NO hace
29
+ *
30
+ * No adivina. No "completa" una card a la que le falta evidencia: la marca
31
+ * como incompleta y deja que un humano o un agente la retome. Un sistema
32
+ * que se auto-repara inventando evidencia es peor que uno que dice "esto
33
+ * esta roto".
34
+ */
35
+ import path from "node:path";
36
+ import fs from "node:fs/promises";
37
+ import { logger } from "./log.js";
38
+ const log = logger("oficina:recovery");
39
+ /** ¿Existe un artefacto de la card? */
40
+ async function artifactExists(cardDir, name) {
41
+ try {
42
+ await fs.access(path.join(cardDir, name));
43
+ return true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ /**
50
+ * Reconcilia la corrida.
51
+ *
52
+ * Devuelve el reporte. Con `autoRepair` aplica las reparaciones que no
53
+ * requieren decision humana (liberar leases, liberar agentes que
54
+ * empezaron la card de otro).
55
+ */
56
+ export async function reconcile(office, options = {}) {
57
+ const autoRepair = options.autoRepair ?? true;
58
+ const dryRun = options.dryRun ?? false;
59
+ const clock = options.clock ?? Date.now;
60
+ const now = new Date(clock()).toISOString();
61
+ const state = await office.loadState();
62
+ const roster = await office.loadRoster(state.runId);
63
+ const issues = [];
64
+ const artifactsRoot = office.paths.artifactsDir;
65
+ const leasesDir = office.paths.leasesDir;
66
+ /* ── Cards vs artefactos ─────────────────────────────────────────────── */
67
+ for (const cardId of state.order) {
68
+ const card = state.cards[cardId];
69
+ if (!card)
70
+ continue;
71
+ const cardDir = path.join(artifactsRoot, cardId);
72
+ // Una card en review sin handoff: la entrega se perdio en el camino.
73
+ if (card.state === "review") {
74
+ const tieneHandoff = await artifactExists(cardDir, "handoff.md");
75
+ const tieneEvidencia = await artifactExists(cardDir, "evidence.md");
76
+ if (!tieneHandoff) {
77
+ issues.push({
78
+ kind: "review-without-artifacts",
79
+ severity: "warning",
80
+ cardId,
81
+ detail: `La card esta en review pero no tiene handoff.md. ` +
82
+ `Lo mas probable es que el agente escribiera el artefacto y muriera antes de entregar.`,
83
+ repair: "Devolver la card a in_progress para que el agente la retome, o revisar el handoff a mano.",
84
+ repaired: false,
85
+ });
86
+ }
87
+ else if (!tieneEvidencia) {
88
+ issues.push({
89
+ kind: "review-without-evidence",
90
+ severity: "warning",
91
+ cardId,
92
+ detail: `La card esta en review sin evidence.md: no hay evidencia de que se haya hecho el trabajo.`,
93
+ repair: "Pedir la evidencia antes de correr los gates.",
94
+ repaired: false,
95
+ });
96
+ }
97
+ }
98
+ // Una card cerrada sin gate: el invariante deberia haberlo impedido.
99
+ if (card.state === "done") {
100
+ const tieneGate = await artifactExists(cardDir, "gates.md");
101
+ if (!tieneGate) {
102
+ issues.push({
103
+ kind: "done-without-gate",
104
+ severity: "critical",
105
+ cardId,
106
+ detail: `La card esta en done pero no tiene gates.md. Se cerro sin gate: ` +
107
+ `el invariante gate-required-before-done fue vulnerado (probablemente por un crash).`,
108
+ repair: "Reabrir la card y correr los gates de verdad.",
109
+ repaired: false,
110
+ });
111
+ }
112
+ }
113
+ // Una card en progreso sin dueño.
114
+ if ((card.state === "in_progress" || card.state === "rework") && !card.owner) {
115
+ issues.push({
116
+ kind: "card-in-progress-without-owner",
117
+ severity: "critical",
118
+ cardId,
119
+ detail: `La card esta en ${card.state} pero no tiene owner. Nadie la va a terminar.`,
120
+ repair: "Liberar la card para que otro agente la reclame.",
121
+ repaired: false,
122
+ });
123
+ }
124
+ // Una dependencia que nunca se va a resolver.
125
+ if (card.state === "blocked") {
126
+ const pendientes = card.dependsOn.filter((d) => {
127
+ const dep = state.cards[d];
128
+ return dep && dep.state !== "done";
129
+ });
130
+ if (pendientes.length > 0) {
131
+ issues.push({
132
+ kind: "blocked-by-unresolvable-dependency",
133
+ severity: "warning",
134
+ cardId,
135
+ detail: `La card esta bloqueada y depende de ${pendientes.join(", ")}, que no estan en done.`,
136
+ repair: "Revisar la dependencia o replanificar.",
137
+ repaired: false,
138
+ });
139
+ }
140
+ }
141
+ }
142
+ /* ── Artefactos huerfanos ────────────────────────────────────────────── */
143
+ let cardDirs = [];
144
+ try {
145
+ cardDirs = (await fs.readdir(artifactsRoot, { withFileTypes: true }))
146
+ .filter((d) => d.isDirectory())
147
+ .map((d) => d.name);
148
+ }
149
+ catch {
150
+ cardDirs = [];
151
+ }
152
+ for (const dirName of cardDirs) {
153
+ if (state.cards[dirName])
154
+ continue;
155
+ issues.push({
156
+ kind: "orphaned-artifact",
157
+ severity: "warning",
158
+ cardId: dirName,
159
+ detail: `Hay artefactos de una card que no existe en el estado (${dirName}).`,
160
+ repair: "Se puede borrar el directorio: no lo va a usar nadie.",
161
+ repaired: false,
162
+ });
163
+ }
164
+ /* ── Leases vs estado ────────────────────────────────────────────────── */
165
+ let leaseFiles = [];
166
+ try {
167
+ leaseFiles = (await fs.readdir(leasesDir)).filter((f) => f.endsWith(".json"));
168
+ }
169
+ catch {
170
+ leaseFiles = [];
171
+ }
172
+ for (const file of leaseFiles) {
173
+ const taskId = file.replace(/\.json$/, "");
174
+ const card = state.cards[taskId];
175
+ if (!card) {
176
+ issues.push({
177
+ kind: "stale-lease",
178
+ severity: "warning",
179
+ cardId: taskId,
180
+ detail: `Hay un lease de una task que no existe en el estado.`,
181
+ repair: "Liberar el lease.",
182
+ repaired: false,
183
+ });
184
+ if (autoRepair && !dryRun) {
185
+ await office.leases.release(taskId, "desconocido").catch(() => false);
186
+ const last = issues[issues.length - 1];
187
+ last.repaired = true;
188
+ }
189
+ continue;
190
+ }
191
+ if (card.state === "done" || card.state === "review") {
192
+ const holder = await office.leases.holder(taskId);
193
+ if (holder) {
194
+ issues.push({
195
+ kind: "stale-lease",
196
+ severity: "warning",
197
+ cardId: taskId,
198
+ agentId: holder,
199
+ detail: `La card esta en ${card.state} pero ${holder} sigue con el lease tomado.`,
200
+ repair: "Liberar el lease.",
201
+ repaired: false,
202
+ });
203
+ if (autoRepair && !dryRun) {
204
+ await office.leases.release(taskId, holder).catch(() => false);
205
+ const last = issues[issues.length - 1];
206
+ last.repaired = true;
207
+ }
208
+ }
209
+ }
210
+ if (!card.owner) {
211
+ issues.push({
212
+ kind: "lease-without-owner",
213
+ severity: "warning",
214
+ cardId: taskId,
215
+ detail: `Hay un lease tomado de una card sin owner.`,
216
+ repair: "Liberar el lease.",
217
+ repaired: false,
218
+ });
219
+ }
220
+ }
221
+ /* ── Roster vs estado ────────────────────────────────────────────────── */
222
+ for (const agent of Object.values(roster.agents)) {
223
+ if (!agent.currentCard)
224
+ continue;
225
+ const card = state.cards[agent.currentCard];
226
+ if (!card) {
227
+ issues.push({
228
+ kind: "stale-lease",
229
+ severity: "warning",
230
+ agentId: agent.id,
231
+ cardId: agent.currentCard,
232
+ detail: `El agente apunta a una card que no existe.`,
233
+ repair: "Liberar el agente.",
234
+ repaired: false,
235
+ });
236
+ continue;
237
+ }
238
+ if (card.state === "done" && agent.state === "working") {
239
+ issues.push({
240
+ kind: "agent-still-working-on-done-card",
241
+ severity: "warning",
242
+ agentId: agent.id,
243
+ cardId: card.id,
244
+ detail: `La card esta en done pero el agente sigue marcado como working.`,
245
+ repair: "Liberar el agente a idle_available.",
246
+ repaired: false,
247
+ });
248
+ if (autoRepair && !dryRun) {
249
+ agent.state = "idle_available";
250
+ agent.currentCard = null;
251
+ agent.canAccept = true;
252
+ agent.idleSince = now;
253
+ await office.saveRoster(roster);
254
+ const last = issues[issues.length - 1];
255
+ last.repaired = true;
256
+ }
257
+ }
258
+ }
259
+ const criticas = issues.filter((i) => i.severity === "critical" && !i.repaired);
260
+ const report = {
261
+ runId: state.runId,
262
+ checkedAt: now,
263
+ issues,
264
+ summary: renderRecoveryReport(issues),
265
+ healthy: criticas.length === 0,
266
+ };
267
+ log.info("reconciliacion completa", {
268
+ runId: state.runId,
269
+ issues: issues.length,
270
+ criticas: criticas.length,
271
+ reparadas: issues.filter((i) => i.repaired).length,
272
+ });
273
+ return report;
274
+ }
275
+ /** Renderiza el reporte como markdown. */
276
+ export function renderRecoveryReport(issues) {
277
+ if (issues.length === 0)
278
+ return "Todo consistente. Sin nada que reconciliar.";
279
+ const lines = [];
280
+ const criticas = issues.filter((i) => i.severity === "critical");
281
+ const avisos = issues.filter((i) => i.severity === "warning");
282
+ if (criticas.length > 0) {
283
+ lines.push(`## Criticos (${criticas.length})`, "");
284
+ for (const i of criticas) {
285
+ lines.push(`- **${i.kind}**${i.cardId ? ` \`${i.cardId}\`` : ""}: ${i.detail}`);
286
+ lines.push(` - Repair: ${i.repair}${i.repaired ? " *(ya reparado)*" : ""}`);
287
+ }
288
+ lines.push("");
289
+ }
290
+ if (avisos.length > 0) {
291
+ lines.push(`## Avisos (${avisos.length})`, "");
292
+ for (const i of avisos) {
293
+ lines.push(`- **${i.kind}**${i.cardId ? ` \`${i.cardId}\`` : ""}: ${i.detail}`);
294
+ lines.push(` - Repair: ${i.repair}${i.repaired ? " *(ya reparado)*" : ""}`);
295
+ }
296
+ }
297
+ return lines.join("\n");
298
+ }
299
+ /**
300
+ * El estado que una card deberia tener, dado lo que hay en disco.
301
+ *
302
+ * Se usa para el caso "el agente escribio y murio": si el handoff esta
303
+ * escrito pero la card sigue en `in_progress`, la card puede pasar a
304
+ * `review` sin que el agente tenga que volver a trabajar.
305
+ */
306
+ export async function inferCardStateFromDisk(office, cardId) {
307
+ const cardDir = path.join(office.paths.artifactsDir, cardId);
308
+ if (await artifactExists(cardDir, "handoff.md")) {
309
+ if (await artifactExists(cardDir, "evidence.md"))
310
+ return "deliverable";
311
+ return "incomplete";
312
+ }
313
+ return "unknown";
314
+ }
315
+ /** Snapshot del estado para detectar cambios inesperados. */
316
+ export function stateFingerprint(state) {
317
+ const parts = state.order.map((id) => {
318
+ const c = state.cards[id];
319
+ if (!c)
320
+ return `${id}:?`;
321
+ return `${id}:${c.state}:${c.owner ?? "-"}:${c.attempts}:${c.rejections}`;
322
+ });
323
+ return parts.join("|");
324
+ }
325
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/core/recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Sandbox: resolución y validación de rutas.
3
+ *
4
+ * ## Por qué existe
5
+ *
6
+ * El guard de scope comparaba strings:
7
+ *
8
+ * const relative = path.relative(projectRoot, absolute)
9
+ * if (!matchesScope(relative, mayTouch)) throw ...
10
+ *
11
+ * Eso tiene tres agujeros, y los tres son reales:
12
+ *
13
+ * 1. **Symlinks.** `src/link.ts` -> `../../etc/secrets.ts`. El path relativo
14
+ * es `src/link.ts`, que matchea `src/**`. El guard autoriza. La
15
+ * escritura ocurre afuera. Verificar el path **después** de resolver los
16
+ * symlinks es la diferencia entre "está en el scope" y "termina en el
17
+ * scope".
18
+ *
19
+ * 2. **`..` con `mayTouch: ["**"]`.** `**` matcheaba cualquier string,
20
+ * incluido `../../etc/passwd`. El `**` tiene que significar "todo el
21
+ * proyecto", nunca "todo el filesystem".
22
+ *
23
+ * 3. **Paths absolutos.** `/etc/passwd` o `C:\Windows\...` tienen un
24
+ * relativo que empieza en `..`, y con `**` pasaban.
25
+ *
26
+ * ## La regla
27
+ *
28
+ * Ninguna ruta se autoriza por comparación de strings. Se resuelve primero
29
+ * (absoluta → normalizada → symlinks resueltos → realpath) y recién ahí se
30
+ * decide. Y la decisión es **fail-closed**: ante cualquier duda, se deniega.
31
+ *
32
+ * ## Qué NO promete
33
+ *
34
+ * Un sandbox a nivel de aplicación no puede⬇️ contener un `bash` que el modelo
35
+ * redacta con un interpreter embebido. Eso sí se cubre, pero con otra
36
+ * técnica: se analiza el comando antes de ejecutarlo y se instalan
37
+ * *tripwires* después. Ver `analyzeShellCommand` y el modulo de deteccion.
38
+ */
39
+ /**
40
+ * Glob mínimo a regex. Soporta `*`, `**` y `?`.
41
+ *
42
+ * El orden de los reemplazos importa: el `?` se convierte ANTES de insertar
43
+ * los patrones de `**`, porque el grupo insertado contiene un `?` literal
44
+ * que, si se convierte después, se rompe y deja de matchear.
45
+ */
46
+ export declare function globToRegex(glob: string): RegExp;
47
+ /** Normaliza un glob: sin `./` inicial, sin `/` inicial, con `/` como separador. */
48
+ export declare function normalizeGlob(glob: string): string;
49
+ /**
50
+ * ¿El path cae dentro de alguno de los globs?
51
+ *
52
+ * Ojo con `**`: matchea todo lo que se le pase. Por eso `checkScope` nunca
53
+ * llama a esta función antes de verificar la contención en el proyecto.
54
+ */
55
+ export declare function matchesScope(relativePath: string, globs: string[]): boolean;
56
+ /**
57
+ * ¿`target` está dentro de `root`?
58
+ *
59
+ * La comparación es sobre paths ya resueltos, y el separador final se exige
60
+ * explícitamente: `/proj-evil` NO está dentro de `/proj`.
61
+ */
62
+ export declare function isInsideRoot(root: string, target: string): boolean;
63
+ /** Relativo con `/` como separador. */
64
+ export declare function toPosix(p: string): string;
65
+ export interface ResolvedTarget {
66
+ /** Path absoluto normalizado lexicograficamente. */
67
+ absolute: string;
68
+ /** Path real: symlinks resueltos, con la cola inexistente re-enganchada. */
69
+ resolved: string;
70
+ /** Relativo al root, del path REAL. "" si es el root mismo. */
71
+ relative: string;
72
+ /** Relativo al root, del path LEXICOGRAFICO. */
73
+ lexicalRelative: string;
74
+ /** Symlinks encontrados en el camino, del mas externo al mas interno. */
75
+ symlinks: string[];
76
+ /** ¿El archivo (o su directorio padre) existe? */
77
+ exists: boolean;
78
+ /** ¿El path lexical esta dentro del root? */
79
+ insideLexically: boolean;
80
+ /** ¿El path real esta dentro del root? */
81
+ insidePhysically: boolean;
82
+ }
83
+ /**
84
+ * Resuelve un path contra el root, deshaciendo symlinks.
85
+ *
86
+ * Devuelve toda la información que `checkScope` necesita para decidir, y
87
+ * no decide: la decision vive en un solo lugar.
88
+ */
89
+ export declare function resolveTarget(root: string, rawPath: string): Promise<ResolvedTarget>;
90
+ /**
91
+ * Archivos que NUNCA se pueden escribir, pase lo que pase.
92
+ *
93
+ * No son negociables por el scope de la card: un `mayTouch: ["**"]` no
94
+ * habilita a tocar el `.npmrc` de alguien ni una clave privada.
95
+ */
96
+ export declare const SENSITIVE_PATTERNS: readonly string[];
97
+ /**
98
+ * Excepciones: archivos que matchean un patrón sensible pero son inocuos.
99
+ *
100
+ * `.env.example` es un template sin secretos; bloquearlo obliga a los
101
+ * agentes a hacer un rodeo por el `.env` real.
102
+ */
103
+ export declare const SENSITIVE_EXCEPTIONS: readonly string[];
104
+ /** ¿El path relativo cae en un patrón sensible y no es una excepción? */
105
+ export declare function isSensitive(relativePath: string): boolean;
106
+ export interface ScopePolicy {
107
+ mayTouch: string[];
108
+ forbidden: string[];
109
+ }
110
+ export type ScopeVerdictKind = "allowed" | "invalid" | "outside-project" | "symlink-escape" | "sensitive" | "forbidden" | "out-of-scope";
111
+ export interface ScopeVerdict {
112
+ allowed: boolean;
113
+ kind: ScopeVerdictKind;
114
+ /** Razón legible en español, lista para mostrarle al modelo. */
115
+ reason: string;
116
+ /** Path real (symlinks resueltos). */
117
+ resolved: string;
118
+ /** Relativo al root, del path real. */
119
+ relative: string;
120
+ /** Symlinks encontrados en el camino. */
121
+ symlinks: string[];
122
+ }
123
+ /**
124
+ * Rutas que la propia oficina usa como canal de entrega.
125
+ *
126
+ * El agente tiene que poder escribir `handoff.md`, `evidence.md`,
127
+ * `review.md` y `gates.md`: son la evidencia de su trabajo. El scope de la
128
+ * card es `src/**`, asi que sin esta excepcion el agente no podria
129
+ * entregar nunca.
130
+ *
131
+ * OJO: solo los directorios de entrega. `state.json`, `roster.json`, el bus
132
+ * y los leases son del motor — que un agente pueda escribir ahi seria
133
+ * falsear la verdad de la corrida.
134
+ */
135
+ export declare const OFFICE_WRITABLE: readonly string[];
136
+ export interface ScopeCheckOptions {
137
+ /** Ignorar la lista de sensibles (solo para uso interno de la oficina). */
138
+ ignoreSensitive?: boolean;
139
+ /** Rutas que el agente puede escribir aunque no esten en `mayTouch`. */
140
+ officeWritable?: readonly string[];
141
+ }
142
+ /**
143
+ * Decide si un path puede escribirse.
144
+ *
145
+ * El orden de las comprobaciones es intencionado: primero lo que invalida
146
+ * la ruta, después lo que la vuelve peligrosa, y recién al final si entra
147
+ * en el scope permitido. Así el error que recibe el modelo dice la verdad
148
+ * mas específica posible.
149
+ */
150
+ export declare function checkScope(root: string, rawPath: string, policy: ScopePolicy, options?: ScopeCheckOptions): Promise<ScopeVerdict>;
151
+ export interface ShellCandidate {
152
+ /** Path afectado por la escritura. */
153
+ target: string;
154
+ /** Verbo que produce la escritura ("redirect", "tee", "cp", ...). */
155
+ op: string;
156
+ /**
157
+ * true si se detecto una escritura pero no se pudo extraer un path
158
+ * confiable. En ese caso hay que armar el tripwire: no se puede
159
+ * autorizar en blindness.
160
+ */
161
+ uncertain: boolean;
162
+ }
163
+ /**
164
+ * Extrae los paths que un comando de shell puede escribir.
165
+ *
166
+ * ## Por qué no es un parser de shell
167
+ *
168
+ * Un parser completo de shell (con here-docs, substituciones, funciones,
169
+ * `eval`, interpreters embebidos) es un proyecto entero y sigue siendo
170
+ * evadible. Lo que hace falta aca es otra cosa: **no dejar pasar en
171
+ * silencio** una escritura que no entendimos.
172
+ *
173
+ * Por eso la funcion devuelve tambien los targets `uncertain`: cuando
174
+ * vemos una escritura pero no sabemos a donde apunta, el comando no se
175
+ * autoriza a ciegas — se deja pasar con el tripwire armed, y el
176
+ * tripwire es el que detecta la escribir fuera del scope despues.
177
+ */
178
+ export declare function analyzeShellCommand(command: string): ShellCandidate[];
179
+ //# sourceMappingURL=sandbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/core/sandbox.ts"],"names":[],"mappings":"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"}