oficina-opencode 0.0.0-stage → 2.0.1

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 (117) hide show
  1. package/DESIGN.md +145 -0
  2. package/LICENSE +21 -0
  3. package/README.md +670 -2
  4. package/agents/oficina-architect.md +72 -0
  5. package/agents/oficina-commander.md +108 -0
  6. package/agents/oficina-critico.md +148 -0
  7. package/agents/oficina-director.md +160 -0
  8. package/agents/oficina-guardian.md +128 -0
  9. package/agents/oficina-integrator.md +98 -0
  10. package/agents/oficina-verificador.md +88 -0
  11. package/commands/oficina.md +36 -0
  12. package/dist/cli.d.ts +12 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +394 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/core/briefing.d.ts +115 -0
  17. package/dist/core/briefing.d.ts.map +1 -0
  18. package/dist/core/briefing.js +410 -0
  19. package/dist/core/briefing.js.map +1 -0
  20. package/dist/core/bus.d.ts +132 -0
  21. package/dist/core/bus.d.ts.map +1 -0
  22. package/dist/core/bus.js +250 -0
  23. package/dist/core/bus.js.map +1 -0
  24. package/dist/core/catalog.d.ts +303 -0
  25. package/dist/core/catalog.d.ts.map +1 -0
  26. package/dist/core/catalog.js +816 -0
  27. package/dist/core/catalog.js.map +1 -0
  28. package/dist/core/dispatch.d.ts +110 -0
  29. package/dist/core/dispatch.d.ts.map +1 -0
  30. package/dist/core/dispatch.js +301 -0
  31. package/dist/core/dispatch.js.map +1 -0
  32. package/dist/core/factory.d.ts +74 -0
  33. package/dist/core/factory.d.ts.map +1 -0
  34. package/dist/core/factory.js +243 -0
  35. package/dist/core/factory.js.map +1 -0
  36. package/dist/core/fsx.d.ts +39 -0
  37. package/dist/core/fsx.d.ts.map +1 -0
  38. package/dist/core/fsx.js +113 -0
  39. package/dist/core/fsx.js.map +1 -0
  40. package/dist/core/gates.d.ts +97 -0
  41. package/dist/core/gates.d.ts.map +1 -0
  42. package/dist/core/gates.js +467 -0
  43. package/dist/core/gates.js.map +1 -0
  44. package/dist/core/leases.d.ts +46 -0
  45. package/dist/core/leases.d.ts.map +1 -0
  46. package/dist/core/leases.js +94 -0
  47. package/dist/core/leases.js.map +1 -0
  48. package/dist/core/log.d.ts +30 -0
  49. package/dist/core/log.d.ts.map +1 -0
  50. package/dist/core/log.js +43 -0
  51. package/dist/core/log.js.map +1 -0
  52. package/dist/core/mesa.d.ts +82 -0
  53. package/dist/core/mesa.d.ts.map +1 -0
  54. package/dist/core/mesa.js +178 -0
  55. package/dist/core/mesa.js.map +1 -0
  56. package/dist/core/office.d.ts +208 -0
  57. package/dist/core/office.d.ts.map +1 -0
  58. package/dist/core/office.js +618 -0
  59. package/dist/core/office.js.map +1 -0
  60. package/dist/core/paths.d.ts +67 -0
  61. package/dist/core/paths.d.ts.map +1 -0
  62. package/dist/core/paths.js +136 -0
  63. package/dist/core/paths.js.map +1 -0
  64. package/dist/core/retry.d.ts +41 -0
  65. package/dist/core/retry.d.ts.map +1 -0
  66. package/dist/core/retry.js +86 -0
  67. package/dist/core/retry.js.map +1 -0
  68. package/dist/core/roster.d.ts +47 -0
  69. package/dist/core/roster.d.ts.map +1 -0
  70. package/dist/core/roster.js +146 -0
  71. package/dist/core/roster.js.map +1 -0
  72. package/dist/core/router.d.ts +67 -0
  73. package/dist/core/router.d.ts.map +1 -0
  74. package/dist/core/router.js +186 -0
  75. package/dist/core/router.js.map +1 -0
  76. package/dist/core/state.d.ts +89 -0
  77. package/dist/core/state.d.ts.map +1 -0
  78. package/dist/core/state.js +303 -0
  79. package/dist/core/state.js.map +1 -0
  80. package/dist/core/types.d.ts +333 -0
  81. package/dist/core/types.d.ts.map +1 -0
  82. package/dist/core/types.js +27 -0
  83. package/dist/core/types.js.map +1 -0
  84. package/dist/hooks/compacting.d.ts +18 -0
  85. package/dist/hooks/compacting.d.ts.map +1 -0
  86. package/dist/hooks/compacting.js +72 -0
  87. package/dist/hooks/compacting.js.map +1 -0
  88. package/dist/hooks/scopeGuard.d.ts +39 -0
  89. package/dist/hooks/scopeGuard.d.ts.map +1 -0
  90. package/dist/hooks/scopeGuard.js +114 -0
  91. package/dist/hooks/scopeGuard.js.map +1 -0
  92. package/dist/hooks/sessionIdle.d.ts +35 -0
  93. package/dist/hooks/sessionIdle.d.ts.map +1 -0
  94. package/dist/hooks/sessionIdle.js +110 -0
  95. package/dist/hooks/sessionIdle.js.map +1 -0
  96. package/dist/hooks/systemTransform.d.ts +18 -0
  97. package/dist/hooks/systemTransform.d.ts.map +1 -0
  98. package/dist/hooks/systemTransform.js +63 -0
  99. package/dist/hooks/systemTransform.js.map +1 -0
  100. package/dist/index.d.ts +36 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +103 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/tools/index.d.ts +22 -0
  105. package/dist/tools/index.d.ts.map +1 -0
  106. package/dist/tools/index.js +854 -0
  107. package/dist/tools/index.js.map +1 -0
  108. package/docs/ARCHITECTURE.md +258 -0
  109. package/docs/QUE-ES.md +337 -0
  110. package/examples/opencode.json +7 -0
  111. package/package.json +81 -4
  112. package/skills/oficina/README.md +120 -0
  113. package/skills/oficina/SKILL.md +251 -0
  114. package/skills/oficina/reference/mesh.md +345 -0
  115. package/skills/oficina/reference/protocol.md +342 -0
  116. package/skills/oficina/reference/roles.md +171 -0
  117. package/skills/oficina/reference/templates.md +278 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Resolución de paths de la oficina.
3
+ *
4
+ * El problema que resuelve: los permisos y las rutas relativas de OpenCode
5
+ * resuelven contra el directorio de trabajo de la sesión, que no siempre es el
6
+ * proyecto. Si la oficina asumiera `cwd`, escribiría sus artefactos en el
7
+ * lugar equivocado y los agentes no se encontrarían.
8
+ *
9
+ * La regla: `PROJECT_ROOT` se detecta por marcadores de proyecto y todos los
10
+ * paths de la oficina son absolutos.
11
+ */
12
+ export declare const PROJECT_MARKERS: string[];
13
+ export interface ResolvedPaths {
14
+ /** Raíz del proyecto. */
15
+ projectRoot: string;
16
+ /** Directorio de la oficina: `<projectRoot>/.oficina`. */
17
+ officeDir: string;
18
+ /** `<officeDir>/bus`. */
19
+ busDir: string;
20
+ /** `<officeDir>/inbox`. */
21
+ inboxDir: string;
22
+ /** `<officeDir>/leases`. */
23
+ leasesDir: string;
24
+ /** `<officeDir>/artifacts`. */
25
+ artifactsDir: string;
26
+ /** `<officeDir>/fichas`. */
27
+ fichasDir: string;
28
+ /** `<officeDir>/roster.json`. */
29
+ rosterPath: string;
30
+ /** `<officeDir>/state.json`. */
31
+ statePath: string;
32
+ /** `<officeDir>/context.md`. */
33
+ contextPath: string;
34
+ /** `<officeDir>/board.md`. */
35
+ boardPath: string;
36
+ /** `<officeDir>/config.json`. */
37
+ configPath: string;
38
+ /** Si se usó el fallback global en vez del project-local. */
39
+ fallback: boolean;
40
+ }
41
+ /** ¿El directorio parece la raíz de un proyecto? */
42
+ export declare function looksLikeProjectRoot(dir: string): Promise<boolean>;
43
+ /**
44
+ * Resuelve el project root.
45
+ *
46
+ * 1. Si `cwd` es un project root → ese.
47
+ * 2. Si no, busca subdirectorios de primer nivel que lo sean. Si hay
48
+ * exactamente uno → ese. Si hay varios → el de mayor cantidad de fuentes.
49
+ * 3. Si no hay ninguno → `cwd`.
50
+ */
51
+ export declare function resolveProjectRoot(cwd: string): Promise<string>;
52
+ /**
53
+ * Resuelve todos los paths de la oficina.
54
+ *
55
+ * Intenta `<projectRoot>/.oficina`. Si no se puede crear (proyecto de solo
56
+ * lectura, sandbox), cae a `~/.oficina/<slug>`.
57
+ */
58
+ export declare function resolvePaths(cwd: string): Promise<ResolvedPaths>;
59
+ /** Path de una card. */
60
+ export declare function cardDir(paths: ResolvedPaths, cardId: string): string;
61
+ /** Path del inbox de un agente. */
62
+ export declare function inboxDirOf(paths: ResolvedPaths, agentId: string): string;
63
+ /** Path de un lease. */
64
+ export declare function leasePath(paths: ResolvedPaths, taskId: string): string;
65
+ /** Convierte un path absoluto a relativo al project root, para artefactos. */
66
+ export declare function relToProject(paths: ResolvedPaths, target: string): string;
67
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,eAAe,UAgB3B,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,yBAAyB;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,oDAAoD;AACpD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BrE;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiCtE;AAED,wBAAwB;AACxB,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,wBAAwB;AACxB,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGzE"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Resolución de paths de la oficina.
3
+ *
4
+ * El problema que resuelve: los permisos y las rutas relativas de OpenCode
5
+ * resuelven contra el directorio de trabajo de la sesión, que no siempre es el
6
+ * proyecto. Si la oficina asumiera `cwd`, escribiría sus artefactos en el
7
+ * lugar equivocado y los agentes no se encontrarían.
8
+ *
9
+ * La regla: `PROJECT_ROOT` se detecta por marcadores de proyecto y todos los
10
+ * paths de la oficina son absolutos.
11
+ */
12
+ import path from "node:path";
13
+ import fs from "node:fs/promises";
14
+ import { exists } from "./fsx.js";
15
+ export const PROJECT_MARKERS = [
16
+ "package.json",
17
+ "pyproject.toml",
18
+ "setup.py",
19
+ "setup.cfg",
20
+ "requirements.txt",
21
+ "Cargo.toml",
22
+ "go.mod",
23
+ "pom.xml",
24
+ "build.gradle",
25
+ "build.gradle.kts",
26
+ "Makefile",
27
+ "composer.json",
28
+ "Gemfile",
29
+ "deno.json",
30
+ ".git",
31
+ ];
32
+ /** ¿El directorio parece la raíz de un proyecto? */
33
+ export async function looksLikeProjectRoot(dir) {
34
+ for (const marker of PROJECT_MARKERS) {
35
+ if (exists(path.join(dir, marker)))
36
+ return true;
37
+ }
38
+ return false;
39
+ }
40
+ /**
41
+ * Resuelve el project root.
42
+ *
43
+ * 1. Si `cwd` es un project root → ese.
44
+ * 2. Si no, busca subdirectorios de primer nivel que lo sean. Si hay
45
+ * exactamente uno → ese. Si hay varios → el de mayor cantidad de fuentes.
46
+ * 3. Si no hay ninguno → `cwd`.
47
+ */
48
+ export async function resolveProjectRoot(cwd) {
49
+ const abs = path.resolve(cwd);
50
+ if (await looksLikeProjectRoot(abs))
51
+ return abs;
52
+ let entries = [];
53
+ try {
54
+ entries = (await fs.readdir(abs, { withFileTypes: true }))
55
+ .filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules")
56
+ .map((e) => e.name);
57
+ }
58
+ catch {
59
+ return abs;
60
+ }
61
+ const candidates = [];
62
+ for (const name of entries) {
63
+ const dir = path.join(abs, name);
64
+ if (await looksLikeProjectRoot(dir)) {
65
+ let weight = 0;
66
+ for (const sub of ["src", "lib", "app", "packages", "server", "client"]) {
67
+ if (exists(path.join(dir, sub)))
68
+ weight += 10;
69
+ }
70
+ candidates.push({ dir, weight });
71
+ }
72
+ }
73
+ if (candidates.length === 0)
74
+ return abs;
75
+ if (candidates.length === 1)
76
+ return candidates[0].dir;
77
+ candidates.sort((a, b) => b.weight - a.weight);
78
+ return candidates[0].dir;
79
+ }
80
+ /**
81
+ * Resuelve todos los paths de la oficina.
82
+ *
83
+ * Intenta `<projectRoot>/.oficina`. Si no se puede crear (proyecto de solo
84
+ * lectura, sandbox), cae a `~/.oficina/<slug>`.
85
+ */
86
+ export async function resolvePaths(cwd) {
87
+ const projectRoot = await resolveProjectRoot(cwd);
88
+ const localOffice = path.join(projectRoot, ".oficina");
89
+ let officeDir = localOffice;
90
+ let fallback = false;
91
+ try {
92
+ await fs.mkdir(localOffice, { recursive: true });
93
+ const probe = path.join(localOffice, ".write-probe");
94
+ await fs.writeFile(probe, "ok");
95
+ await fs.rm(probe, { force: true });
96
+ }
97
+ catch {
98
+ const slug = path.basename(projectRoot).replace(/[^a-zA-Z0-9._-]/g, "-") || "proyecto";
99
+ officeDir = path.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".oficina", slug);
100
+ await fs.mkdir(officeDir, { recursive: true });
101
+ fallback = true;
102
+ }
103
+ return {
104
+ projectRoot,
105
+ officeDir,
106
+ busDir: path.join(officeDir, "bus"),
107
+ inboxDir: path.join(officeDir, "inbox"),
108
+ leasesDir: path.join(officeDir, "leases"),
109
+ artifactsDir: path.join(officeDir, "artifacts"),
110
+ fichasDir: path.join(officeDir, "fichas"),
111
+ rosterPath: path.join(officeDir, "roster.json"),
112
+ statePath: path.join(officeDir, "state.json"),
113
+ contextPath: path.join(officeDir, "context.md"),
114
+ boardPath: path.join(officeDir, "board.md"),
115
+ configPath: path.join(officeDir, "config.json"),
116
+ fallback,
117
+ };
118
+ }
119
+ /** Path de una card. */
120
+ export function cardDir(paths, cardId) {
121
+ return path.join(paths.artifactsDir, cardId);
122
+ }
123
+ /** Path del inbox de un agente. */
124
+ export function inboxDirOf(paths, agentId) {
125
+ return path.join(paths.inboxDir, agentId);
126
+ }
127
+ /** Path de un lease. */
128
+ export function leasePath(paths, taskId) {
129
+ return path.join(paths.leasesDir, `${taskId}.json`);
130
+ }
131
+ /** Convierte un path absoluto a relativo al project root, para artefactos. */
132
+ export function relToProject(paths, target) {
133
+ const rel = path.relative(paths.projectRoot, target);
134
+ return rel.startsWith("..") ? target : rel;
135
+ }
136
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Retry: la política de 3 golpes.
3
+ *
4
+ * Un rechazo de review no es un evento, es un estado. Un agente que "no
5
+ * cumple" y se le deja reintentar sin límite produce un loop infinito que
6
+ * quema presupuesto sin avanzar. Los contadores viven acá, en código.
7
+ *
8
+ * rechazo 1 → REWORK (mismo owner, con el feedback del review)
9
+ * rechazo 2 → REPLAN (el Director rehace la card: puede cambiar owner,
10
+ * scope o enfoque; si el owner ya falló dos veces,
11
+ * se rota)
12
+ * rechazo 3 → ESCALATE (al usuario, con opciones concretas)
13
+ */
14
+ import type { Card, Limits, OfficeState } from "./types.js";
15
+ export type RetryAction = "rework" | "replan" | "escalate" | "approve";
16
+ export interface RetryDecision {
17
+ action: RetryAction;
18
+ /** Qué tiene que pasar, en texto, para el siguiente intento. */
19
+ instruction: string;
20
+ /** Si el owner debe cambiar. */
21
+ rotateOwner: boolean;
22
+ reason: string;
23
+ }
24
+ /** Calcula la próxima acción dado el estado de rechazo de una card. */
25
+ export declare function decideRetry(card: Card, limits?: Limits): RetryDecision;
26
+ /** Registra un rechazo y devuelve la card actualizada. */
27
+ export declare function recordRejection(card: Card, reason: string): Card;
28
+ /** Registra una aprobación. */
29
+ export declare function recordApproval(card: Card, note: string): Card;
30
+ /** Suma el presupuesto de llamadas a subagentes. */
31
+ export declare function consumeAgentCall(state: OfficeState, calls?: number): void;
32
+ /** Suma el presupuesto de cards. */
33
+ export declare function consumeCard(state: OfficeState): void;
34
+ /** Suma el presupuesto de spawns. */
35
+ export declare function consumeSpawn(state: OfficeState): void;
36
+ /** ¿Queda presupuesto? */
37
+ export declare function hasBudget(state: OfficeState, limits: Limits): {
38
+ ok: boolean;
39
+ reason: string;
40
+ };
41
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/core/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG3D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;AAEtE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAA;IACnB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,WAAW,EAAE,OAAO,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,MAAqB,GAAG,aAAa,CAwBpF;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKhE;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAG7D;AAED,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,SAAI,GAAG,IAAI,CAGpE;AAED,oCAAoC;AACpC,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAGpD;AAED,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAGrD;AAED,0BAA0B;AAC1B,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAiB7F"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Retry: la política de 3 golpes.
3
+ *
4
+ * Un rechazo de review no es un evento, es un estado. Un agente que "no
5
+ * cumple" y se le deja reintentar sin límite produce un loop infinito que
6
+ * quema presupuesto sin avanzar. Los contadores viven acá, en código.
7
+ *
8
+ * rechazo 1 → REWORK (mismo owner, con el feedback del review)
9
+ * rechazo 2 → REPLAN (el Director rehace la card: puede cambiar owner,
10
+ * scope o enfoque; si el owner ya falló dos veces,
11
+ * se rota)
12
+ * rechazo 3 → ESCALATE (al usuario, con opciones concretas)
13
+ */
14
+ import { nowIso } from "./fsx.js";
15
+ /** Calcula la próxima acción dado el estado de rechazo de una card. */
16
+ export function decideRetry(card, limits = {}) {
17
+ const max = limits.maxReviewRejections ?? 3;
18
+ const rejections = card.rejections;
19
+ if (rejections <= 0) {
20
+ return { action: "approve", instruction: "", rotateOwner: false, reason: "sin rechazos previos" };
21
+ }
22
+ if (rejections < max) {
23
+ const rotate = rejections >= 2;
24
+ return {
25
+ action: rejections === 1 ? "rework" : "replan",
26
+ instruction: rotate
27
+ ? "Rehacé la card desde cero: revisá el scope, elegí un owner distinto y atacá el problema de otra forma. No repitas el intento anterior."
28
+ : "Corregí exactamente lo que el review marcó. No reescribas lo que ya estaba bien.",
29
+ rotateOwner: rotate,
30
+ reason: `${rejections} de ${max} rechazos`,
31
+ };
32
+ }
33
+ return {
34
+ action: "escalate",
35
+ instruction: "",
36
+ rotateOwner: true,
37
+ reason: `${rejections} rechazos agotaron el máximo de ${max}`,
38
+ };
39
+ }
40
+ /** Registra un rechazo y devuelve la card actualizada. */
41
+ export function recordRejection(card, reason) {
42
+ card.rejections += 1;
43
+ card.attempts += 1;
44
+ card.result = `RECHAZADO (${card.rejections}): ${reason}`;
45
+ return card;
46
+ }
47
+ /** Registra una aprobación. */
48
+ export function recordApproval(card, note) {
49
+ card.result = `APROBADO: ${note}`;
50
+ return card;
51
+ }
52
+ /** Suma el presupuesto de llamadas a subagentes. */
53
+ export function consumeAgentCall(state, calls = 1) {
54
+ state.budget.agentCallsUsed += calls;
55
+ state.updatedAt = nowIso();
56
+ }
57
+ /** Suma el presupuesto de cards. */
58
+ export function consumeCard(state) {
59
+ state.budget.cardsUsed += 1;
60
+ state.updatedAt = nowIso();
61
+ }
62
+ /** Suma el presupuesto de spawns. */
63
+ export function consumeSpawn(state) {
64
+ state.budget.spawnsUsed += 1;
65
+ state.updatedAt = nowIso();
66
+ }
67
+ /** ¿Queda presupuesto? */
68
+ export function hasBudget(state, limits) {
69
+ if (state.budget.cardsUsed >= state.budget.maxCards) {
70
+ return { ok: false, reason: `presupuesto de cards agotado (${state.budget.cardsUsed}/${state.budget.maxCards})` };
71
+ }
72
+ if (state.budget.agentCallsUsed >= state.budget.maxAgentCalls) {
73
+ return {
74
+ ok: false,
75
+ reason: `presupuesto de invocaciones agotado (${state.budget.agentCallsUsed}/${state.budget.maxAgentCalls})`,
76
+ };
77
+ }
78
+ if (state.budget.spawnsUsed >= state.budget.maxSpawnsTotal) {
79
+ return {
80
+ ok: false,
81
+ reason: `presupuesto de spawns agotado (${state.budget.spawnsUsed}/${state.budget.maxSpawnsTotal})`,
82
+ };
83
+ }
84
+ return { ok: true, reason: "presupuesto disponible" };
85
+ }
86
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/core/retry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Roster: la single source of truth de quién existe y qué está haciendo.
3
+ *
4
+ * Todos lo leen, nadie lo re-declara. El Integrador lo mantiene; los
5
+ * especialistas se auto-registran y reportan estado.
6
+ */
7
+ import type { AgentState, Roster, RosterEntry, Ficha, FichaSource } from "./types.js";
8
+ export declare class RosterStore {
9
+ private readonly rosterPath;
10
+ private readonly clock;
11
+ constructor(rosterPath: string, clock?: () => number);
12
+ /** Lee el roster. Si no existe, uno vacío. */
13
+ load(runId: string): Promise<Roster>;
14
+ /** Escribe el roster. */
15
+ save(roster: Roster): Promise<void>;
16
+ /** Registra o actualiza un agente a partir de su ficha. */
17
+ upsert(roster: Roster, ficha: Ficha, state?: AgentState): Promise<RosterEntry>;
18
+ /** Cambia el estado de un agente. */
19
+ setState(roster: Roster, agentId: string, state: AgentState): RosterEntry | undefined;
20
+ /** Marca heartbeat. Un agente sin heartbeat está muerto. */
21
+ heartbeat(roster: Roster, agentId: string): void;
22
+ /** Asigna una card a un agente. */
23
+ assign(roster: Roster, agentId: string, cardId: string): void;
24
+ /** Libera al agente. Vuelve al pool si corresponde. */
25
+ release(roster: Roster, agentId: string): void;
26
+ /** Agentes disponibles para aceptar trabajo. */
27
+ idlePool(roster: Roster): RosterEntry[];
28
+ /** Agentes trabajando. */
29
+ workingAgents(roster: Roster): RosterEntry[];
30
+ /**
31
+ * Agentes cuyo heartbeat venció.
32
+ *
33
+ * Un agente que no da señales está muerto: su card se reasigna y su lease
34
+ * expira. Sin esto, una corrida queda trabada por un subagente que murió
35
+ * en silencio.
36
+ */
37
+ deadAgents(roster: Roster, timeoutSec: number): RosterEntry[];
38
+ /** Registra que un agente ayudó a otro (anti-monotonía). */
39
+ recordHelp(roster: Roster, helperId: string, helpedId: string): void;
40
+ /** Ubica un agente por rol. */
41
+ findByRole(roster: Roster, role: string): RosterEntry | undefined;
42
+ /** Agentes que tienen una capability dada. */
43
+ findByCapability(roster: Roster, capability: string): RosterEntry[];
44
+ }
45
+ /** Construye la fuente de una ficha desde el roster. */
46
+ export declare function sourceOf(entry: RosterEntry): FichaSource;
47
+ //# sourceMappingURL=roster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roster.d.ts","sourceRoot":"","sources":["../../src/core/roster.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGrF,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,MAAM,MAAiB;IAGjD,8CAA8C;IACxC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1C,yBAAyB;IACnB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzC,2DAA2D;IACrD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAE,UAAuB,GAAG,OAAO,CAAC,WAAW,CAAC;IA4BhG,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS;IAUrF,4DAA4D;IAC5D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAKhD,mCAAmC;IACnC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAS7D,uDAAuD;IACvD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAU9C,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE;IAMvC,0BAA0B;IAC1B,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE;IAI5C;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE;IAU7D,4DAA4D;IAC5D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAUpE,+BAA+B;IAC/B,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIjE,8CAA8C;IAC9C,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE;CAGpE;AAED,wDAAwD;AACxD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAExD"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Roster: la single source of truth de quién existe y qué está haciendo.
3
+ *
4
+ * Todos lo leen, nadie lo re-declara. El Integrador lo mantiene; los
5
+ * especialistas se auto-registran y reportan estado.
6
+ */
7
+ import { nowIso, readJson, writeJsonAtomic } from "./fsx.js";
8
+ export class RosterStore {
9
+ rosterPath;
10
+ clock;
11
+ constructor(rosterPath, clock = Date.now) {
12
+ this.rosterPath = rosterPath;
13
+ this.clock = clock;
14
+ }
15
+ /** Lee el roster. Si no existe, uno vacío. */
16
+ async load(runId) {
17
+ const existing = await readJson(this.rosterPath);
18
+ if (existing)
19
+ return existing;
20
+ return {
21
+ version: 2,
22
+ runId,
23
+ agents: {},
24
+ updatedAt: nowIso(this.clock),
25
+ };
26
+ }
27
+ /** Escribe el roster. */
28
+ async save(roster) {
29
+ roster.updatedAt = nowIso(this.clock);
30
+ await writeJsonAtomic(this.rosterPath, roster);
31
+ }
32
+ /** Registra o actualiza un agente a partir de su ficha. */
33
+ async upsert(roster, ficha, state = "deployed") {
34
+ const existing = roster.agents[ficha.id];
35
+ const entry = existing ?? {
36
+ id: ficha.id,
37
+ role: ficha.role,
38
+ displayName: ficha.displayName,
39
+ state,
40
+ capabilities: [...ficha.capabilities],
41
+ fichaPath: "",
42
+ currentCard: null,
43
+ lease: null,
44
+ idleSince: null,
45
+ canAccept: true,
46
+ helpGiven: 0,
47
+ helpReceived: 0,
48
+ helpedPeers: [],
49
+ lastHeartbeat: nowIso(this.clock),
50
+ source: ficha.source,
51
+ };
52
+ entry.role = ficha.role;
53
+ entry.displayName = ficha.displayName;
54
+ entry.capabilities = [...ficha.capabilities];
55
+ entry.source = ficha.source;
56
+ entry.lastHeartbeat = nowIso(this.clock);
57
+ roster.agents[ficha.id] = entry;
58
+ return entry;
59
+ }
60
+ /** Cambia el estado de un agente. */
61
+ setState(roster, agentId, state) {
62
+ const entry = roster.agents[agentId];
63
+ if (!entry)
64
+ return undefined;
65
+ entry.state = state;
66
+ entry.idleSince = state === "idle_available" ? nowIso(this.clock) : null;
67
+ entry.canAccept = state === "idle_available";
68
+ entry.lastHeartbeat = nowIso(this.clock);
69
+ return entry;
70
+ }
71
+ /** Marca heartbeat. Un agente sin heartbeat está muerto. */
72
+ heartbeat(roster, agentId) {
73
+ const entry = roster.agents[agentId];
74
+ if (entry)
75
+ entry.lastHeartbeat = nowIso(this.clock);
76
+ }
77
+ /** Asigna una card a un agente. */
78
+ assign(roster, agentId, cardId) {
79
+ const entry = roster.agents[agentId];
80
+ if (!entry)
81
+ return;
82
+ entry.currentCard = cardId;
83
+ entry.state = "working";
84
+ entry.canAccept = false;
85
+ entry.idleSince = null;
86
+ }
87
+ /** Libera al agente. Vuelve al pool si corresponde. */
88
+ release(roster, agentId) {
89
+ const entry = roster.agents[agentId];
90
+ if (!entry)
91
+ return;
92
+ entry.currentCard = null;
93
+ entry.lease = null;
94
+ if (entry.state !== "retired" && entry.state !== "blocked") {
95
+ this.setState(roster, agentId, "idle_available");
96
+ }
97
+ }
98
+ /** Agentes disponibles para aceptar trabajo. */
99
+ idlePool(roster) {
100
+ return Object.values(roster.agents)
101
+ .filter((a) => a.state === "idle_available" && a.canAccept && a.lease === null)
102
+ .sort((a, b) => (a.idleSince ?? "").localeCompare(b.idleSince ?? ""));
103
+ }
104
+ /** Agentes trabajando. */
105
+ workingAgents(roster) {
106
+ return Object.values(roster.agents).filter((a) => a.state === "working");
107
+ }
108
+ /**
109
+ * Agentes cuyo heartbeat venció.
110
+ *
111
+ * Un agente que no da señales está muerto: su card se reasigna y su lease
112
+ * expira. Sin esto, una corrida queda trabada por un subagente que murió
113
+ * en silencio.
114
+ */
115
+ deadAgents(roster, timeoutSec) {
116
+ const cutoff = this.clock() - timeoutSec * 1000;
117
+ return Object.values(roster.agents).filter((a) => a.state !== "retired" &&
118
+ a.state !== "idle_available" &&
119
+ Date.parse(a.lastHeartbeat) < cutoff);
120
+ }
121
+ /** Registra que un agente ayudó a otro (anti-monotonía). */
122
+ recordHelp(roster, helperId, helpedId) {
123
+ const helper = roster.agents[helperId];
124
+ const helped = roster.agents[helpedId];
125
+ if (helper) {
126
+ helper.helpGiven += 1;
127
+ if (!helper.helpedPeers.includes(helpedId))
128
+ helper.helpedPeers.push(helpedId);
129
+ }
130
+ if (helped)
131
+ helped.helpReceived += 1;
132
+ }
133
+ /** Ubica un agente por rol. */
134
+ findByRole(roster, role) {
135
+ return Object.values(roster.agents).find((a) => a.role === role);
136
+ }
137
+ /** Agentes que tienen una capability dada. */
138
+ findByCapability(roster, capability) {
139
+ return Object.values(roster.agents).filter((a) => a.capabilities.includes(capability));
140
+ }
141
+ }
142
+ /** Construye la fuente de una ficha desde el roster. */
143
+ export function sourceOf(entry) {
144
+ return entry.source;
145
+ }
146
+ //# sourceMappingURL=roster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roster.js","sourceRoot":"","sources":["../../src/core/roster.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Router: qué agente hace qué card, y con qué rotación.
3
+ *
4
+ * Dos reglas hardcoded que vienen de los síntomas reportados:
5
+ *
6
+ * 1. **El revisor nunca es el owner.** Sin auto-aprobación.
7
+ * 2. **Rotación.** Si el mismo agente sería owner de 3 cards del mismo tipo
8
+ * seguidas, la cuarta la toma otro. El "subagente monotono" es un bug de
9
+ * routing, no de capacidad.
10
+ *
11
+ * La extensión: la rotación también aplica a la ayuda. Si el par A↔B ya se
12
+ * ayudaron dos veces, el tercer pedido se enruta a un tercero.
13
+ */
14
+ import type { Card, CatalogAgent, Ficha, Roster, RosterEntry } from "./types.js";
15
+ export interface RouteOptions {
16
+ /** Un agente con este rol ya hizo N cards del mismo tipo seguidos. */
17
+ ownerStreak?: number;
18
+ /** Máximo de cards del mismo tipo con el mismo owner antes de rotar. */
19
+ rotationThreshold?: number;
20
+ /** Pares que ya colaboraron N veces. */
21
+ pairCollaborationCount?: number;
22
+ /** Máximo de colaboraciones por par antes de enrutar a un tercero. */
23
+ pairRotationThreshold?: number;
24
+ }
25
+ export interface RouteResult {
26
+ owner: RosterEntry | null;
27
+ reviewer: RosterEntry | null;
28
+ /** Por qué se eligió ese owner. */
29
+ reason: string;
30
+ /** Si no hay owner posible, qué haría falta. */
31
+ missing: string[];
32
+ }
33
+ /** Tipo de card → rol canónico que debería ejecutarla. */
34
+ export declare const ROLE_BY_CARD_TYPE: Record<Card["type"], string[]>;
35
+ export declare const DEFAULT_ROTATION_THRESHOLD = 3;
36
+ export declare const DEFAULT_PAIR_ROTATION_THRESHOLD = 2;
37
+ /**
38
+ * Elige owner y reviewer para una card.
39
+ *
40
+ * Preferencia de owner:
41
+ * 1. Rol canónico del tipo de card (si está en el roster y está libre).
42
+ * 2. Mejor match por capabilities entre los agentes libres.
43
+ * 3. Agente idle del pool (para repartir carga).
44
+ *
45
+ * El reviewer se elige entre los que NO son el owner, priorizando los que no
46
+ * hayan reciente-compliant colaborado con el owner (umbral de anti-monotonía).
47
+ */
48
+ export declare function routeCard(card: Card, roster: Roster, catalog?: CatalogAgent[], options?: RouteOptions): RouteResult;
49
+ /**
50
+ * A quién enrutar un pedido de ayuda.
51
+ *
52
+ * 1. Los peers declarados en la ficha que estén libres.
53
+ * 2. Un tercero si el par ya colaboró demasiado (anti-monotonía extendida).
54
+ */
55
+ export declare function routeHelp(requesterId: string, roster: Roster, requesterPeers?: string[], options?: RouteOptions): {
56
+ targets: string[];
57
+ reason: string;
58
+ };
59
+ /**
60
+ * Calcula la racha de cards del mismo tipo con el mismo owner.
61
+ *
62
+ * Se usa para decidir rotación.
63
+ */
64
+ export declare function ownerStreak(cards: Record<string, Card> | Card[], order: string[], ownerId: string, type: Card["type"]): number;
65
+ /** Construye la ficha de un agente de roster (para renderizar artefactos). */
66
+ export declare function fichaFromEntry(entry: RosterEntry, source?: Ficha["source"]): Ficha;
67
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/core/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGhF,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,wCAAwC;IACxC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,sEAAsE;IACtE,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,0DAA0D;AAC1D,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAU5D,CAAA;AAED,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAC3C,eAAO,MAAM,+BAA+B,IAAI,CAAA;AAEhD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,YAAY,EAAO,EAC5B,OAAO,GAAE,YAAiB,GACzB,WAAW,CAwFb;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,cAAc,GAAE,MAAM,EAAO,EAC7B,OAAO,GAAE,YAAiB,GACzB;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAgCvC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE,EACpC,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,GACjB,MAAM,CAcR;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAgBlF"}