oficina-opencode 3.0.5 → 3.1.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 (145) hide show
  1. package/README.md +240 -208
  2. package/commands/oficina.md +7 -6
  3. package/dashboard/.dom-check.html +1257 -0
  4. package/dashboard/README-DASHBOARD.md +64 -0
  5. package/dashboard/assets/factory.css +1543 -0
  6. package/dashboard/assets/tokens.css +208 -0
  7. package/dashboard/build-kanban-data.mjs +279 -0
  8. package/dashboard/dashboard.css +753 -0
  9. package/dashboard/dashboard.html +121 -0
  10. package/dashboard/dashboard.js +610 -0
  11. package/dashboard/data/snapshot.json +883 -0
  12. package/dashboard/debate.html +134 -0
  13. package/dashboard/factory/build-snapshot.mjs +89 -0
  14. package/dashboard/factory/factory-app.js +1185 -0
  15. package/dashboard/factory/factory-core.js +541 -0
  16. package/dashboard/factory/snapshot.js +850 -0
  17. package/dashboard/factory.html +350 -0
  18. package/dashboard/kanban-data.json +671 -0
  19. package/dashboard/kanban-source.js +643 -0
  20. package/dashboard/kanban.check.mjs +454 -0
  21. package/dashboard/kanban.html +1257 -0
  22. package/dashboard/leadership.html +1008 -0
  23. package/dashboard/lib/debate-app.js +1149 -0
  24. package/dashboard/lib/debate-core.js +698 -0
  25. package/dashboard/lib/debate-data.js +163 -0
  26. package/dashboard/lib/debate-demo.js +110 -0
  27. package/dashboard/lib/debate.css +1155 -0
  28. package/dashboard/lib/leadership-model.js +946 -0
  29. package/dashboard/probe.mjs +35 -0
  30. package/dashboard/scripts/serve.mjs +122 -0
  31. package/dashboard/scripts/snapshot-data.mjs +93 -0
  32. package/dashboard/scripts/snapshot.mjs +28 -0
  33. package/dashboard/server.mjs +242 -0
  34. package/dashboard/shot.mjs +54 -0
  35. package/dashboard/shots/desktop-gates.png +0 -0
  36. package/dashboard/shots/desktop-light.png +0 -0
  37. package/dashboard/shots/desktop-real.png +0 -0
  38. package/dashboard/shots/mobile.png +0 -0
  39. package/dashboard/test/factory-core.test.mjs +244 -0
  40. package/dashboard/tests/debate-core.test.mjs +336 -0
  41. package/dashboard/tests/leadership-model.test.mjs +404 -0
  42. package/dist/cli.js +148 -26
  43. package/dist/cli.js.map +1 -1
  44. package/dist/core/bus.js +3 -3
  45. package/dist/core/bus.js.map +1 -1
  46. package/dist/core/catalog.d.ts +9 -1
  47. package/dist/core/catalog.d.ts.map +1 -1
  48. package/dist/core/catalog.js +77 -2
  49. package/dist/core/catalog.js.map +1 -1
  50. package/dist/core/dashboard.d.ts +33 -0
  51. package/dist/core/dashboard.d.ts.map +1 -0
  52. package/dist/core/dashboard.js +115 -0
  53. package/dist/core/dashboard.js.map +1 -0
  54. package/dist/core/dispatch.d.ts +1 -1
  55. package/dist/core/dispatch.d.ts.map +1 -1
  56. package/dist/core/dispatch.js +84 -12
  57. package/dist/core/dispatch.js.map +1 -1
  58. package/dist/core/docsync.d.ts +0 -9
  59. package/dist/core/docsync.d.ts.map +1 -1
  60. package/dist/core/docsync.js +12 -15
  61. package/dist/core/docsync.js.map +1 -1
  62. package/dist/core/environment.d.ts +28 -0
  63. package/dist/core/environment.d.ts.map +1 -0
  64. package/dist/core/environment.js +80 -0
  65. package/dist/core/environment.js.map +1 -0
  66. package/dist/core/factory.d.ts +16 -0
  67. package/dist/core/factory.d.ts.map +1 -1
  68. package/dist/core/factory.js +143 -0
  69. package/dist/core/factory.js.map +1 -1
  70. package/dist/core/fsx.d.ts +20 -2
  71. package/dist/core/fsx.d.ts.map +1 -1
  72. package/dist/core/fsx.js +10 -6
  73. package/dist/core/fsx.js.map +1 -1
  74. package/dist/core/gates.d.ts +23 -1
  75. package/dist/core/gates.d.ts.map +1 -1
  76. package/dist/core/gates.js +83 -8
  77. package/dist/core/gates.js.map +1 -1
  78. package/dist/core/leases.d.ts.map +1 -1
  79. package/dist/core/leases.js +4 -1
  80. package/dist/core/leases.js.map +1 -1
  81. package/dist/core/mesa.d.ts +1 -6
  82. package/dist/core/mesa.d.ts.map +1 -1
  83. package/dist/core/mesa.js +1 -6
  84. package/dist/core/mesa.js.map +1 -1
  85. package/dist/core/office.d.ts +3 -29
  86. package/dist/core/office.d.ts.map +1 -1
  87. package/dist/core/office.js +77 -90
  88. package/dist/core/office.js.map +1 -1
  89. package/dist/core/paths.d.ts.map +1 -1
  90. package/dist/core/paths.js +6 -1
  91. package/dist/core/paths.js.map +1 -1
  92. package/dist/core/retry.d.ts +2 -8
  93. package/dist/core/retry.d.ts.map +1 -1
  94. package/dist/core/retry.js +3 -34
  95. package/dist/core/retry.js.map +1 -1
  96. package/dist/core/roster.js +3 -3
  97. package/dist/core/roster.js.map +1 -1
  98. package/dist/core/sandbox.d.ts +38 -1
  99. package/dist/core/sandbox.d.ts.map +1 -1
  100. package/dist/core/sandbox.js +119 -15
  101. package/dist/core/sandbox.js.map +1 -1
  102. package/dist/core/state.d.ts +2 -2
  103. package/dist/core/state.d.ts.map +1 -1
  104. package/dist/core/state.js +5 -33
  105. package/dist/core/state.js.map +1 -1
  106. package/dist/core/stores.d.ts +3 -13
  107. package/dist/core/stores.d.ts.map +1 -1
  108. package/dist/core/stores.js +8 -32
  109. package/dist/core/stores.js.map +1 -1
  110. package/dist/core/systemPrompt.d.ts.map +1 -1
  111. package/dist/core/systemPrompt.js +4 -1
  112. package/dist/core/systemPrompt.js.map +1 -1
  113. package/dist/core/tripwire.d.ts +22 -8
  114. package/dist/core/tripwire.d.ts.map +1 -1
  115. package/dist/core/tripwire.js +71 -26
  116. package/dist/core/tripwire.js.map +1 -1
  117. package/dist/core/types.d.ts +6 -14
  118. package/dist/core/types.d.ts.map +1 -1
  119. package/dist/core/types.js +11 -3
  120. package/dist/core/types.js.map +1 -1
  121. package/dist/hooks/scopeGuard.d.ts +15 -1
  122. package/dist/hooks/scopeGuard.d.ts.map +1 -1
  123. package/dist/hooks/scopeGuard.js +88 -73
  124. package/dist/hooks/scopeGuard.js.map +1 -1
  125. package/dist/tools/index.d.ts +9 -1
  126. package/dist/tools/index.d.ts.map +1 -1
  127. package/dist/tools/index.js +29 -81
  128. package/dist/tools/index.js.bak +972 -0
  129. package/dist/tools/index.js.map +1 -1
  130. package/dist/v1.d.ts.map +1 -1
  131. package/dist/v1.js +30 -36
  132. package/dist/v1.js.map +1 -1
  133. package/dist/v2.d.ts +5 -0
  134. package/dist/v2.d.ts.map +1 -1
  135. package/dist/v2.js +79 -46
  136. package/dist/v2.js.bak +517 -0
  137. package/dist/v2.js.map +1 -1
  138. package/docs/GARANTIAS.md +2 -2
  139. package/docs/PRUEBA-E2E.md +4 -4
  140. package/docs/QUE-ES.md +4 -5
  141. package/docs/SEGURIDAD-REPO.md +83 -0
  142. package/docs/assets/banner.svg +42 -0
  143. package/docs/catalog-facts.json +3 -3
  144. package/docs/superpowers/specs/2026-10-29-factory-skill-creator-and-remove-budget.md +182 -0
  145. package/package.json +14 -6
@@ -0,0 +1,163 @@
1
+ /*
2
+ * debate-data.js — acceso a datos del panel de debate (T-05).
3
+ *
4
+ * Tres fuentes, en orden de preferencia:
5
+ * 1. `server` → GET /api/debate de dashboard/server.mjs (lectura en vivo de
6
+ * .oficina/bus/**. Es la única que ve archivos nuevos.)
7
+ * 2. `snapshot`→ un JSON servido por URL (?src=https://...)
8
+ * 3. `files` → JSON arrastrados/soltados por el usuario (file:// no puede
9
+ * listar directorios ni leer .oficina por CORS)
10
+ *
11
+ * Publica `globalThis.DebateData`. No toca DOM.
12
+ */
13
+ (function (root) {
14
+ "use strict";
15
+
16
+ var CORE = root.DebateCore;
17
+ var DEFAULT_TIMEOUT_MS = 5000;
18
+
19
+ function fetchJson(url, timeoutMs) {
20
+ var controller = typeof AbortController === "function" ? new AbortController() : null;
21
+ var timer = null;
22
+ var options = { cache: "no-store", headers: { Accept: "application/json" } };
23
+ if (controller) options.signal = controller.signal;
24
+ if (controller) {
25
+ timer = setTimeout(function () {
26
+ controller.abort();
27
+ }, timeoutMs || DEFAULT_TIMEOUT_MS);
28
+ }
29
+ return fetch(url, options)
30
+ .then(function (res) {
31
+ if (!res.ok) throw new Error("HTTP " + res.status + " en " + url);
32
+ return res.json();
33
+ })
34
+ .finally(function () {
35
+ if (timer) clearTimeout(timer);
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Decide de dónde leer según el entorno y la URL.
41
+ * ?demo=1 → datos sintéticos (se rotula en la UI)
42
+ * ?src=<url> → snapshot JSON estático
43
+ * ?api=<url> → base del server local
44
+ * file:// → archivos soltados a mano
45
+ * http(s):// → server local
46
+ */
47
+ function detectMode(locationLike) {
48
+ var loc = locationLike || (typeof location !== "undefined" ? location : { protocol: "http:", search: "" });
49
+ var params = new URLSearchParams(loc.search || "");
50
+ if (params.get("demo") === "1") {
51
+ return { mode: "demo", reason: "?demo=1", apiBase: null, srcUrl: null };
52
+ }
53
+ var src = params.get("src");
54
+ if (src) return { mode: "snapshot", reason: "?src=" + src, apiBase: null, srcUrl: src };
55
+
56
+ var api = params.get("api");
57
+ if (api) return { mode: "server", reason: "?api=" + api, apiBase: api.replace(/\/+$/, ""), srcUrl: null };
58
+
59
+ if (loc.protocol === "file:") {
60
+ return { mode: "files", reason: "file://", apiBase: null, srcUrl: null };
61
+ }
62
+ return { mode: "server", reason: "mismo origen", apiBase: "", srcUrl: null };
63
+ }
64
+
65
+ function loadFromServer(apiBase) {
66
+ var base = apiBase == null ? "" : String(apiBase).replace(/\/+$/, "");
67
+ return fetchJson(base + "/api/debate").then(function (payload) {
68
+ var snapshot = CORE.readSnapshot(payload);
69
+ snapshot.meta = snapshot.meta || {};
70
+ snapshot.meta.source = "server";
71
+ return snapshot;
72
+ });
73
+ }
74
+
75
+ function loadFromUrl(url) {
76
+ return fetchJson(url).then(function (payload) {
77
+ var snapshot = CORE.readSnapshot(payload);
78
+ snapshot.meta = snapshot.meta || {};
79
+ snapshot.meta.source = "snapshot";
80
+ snapshot.meta.sourceUrl = url;
81
+ return snapshot;
82
+ });
83
+ }
84
+
85
+ /** Combina snapshots parciales (varios archivos sueltos del bus). */
86
+ function mergeSnapshots(list) {
87
+ var merged = { messages: [], agents: {}, cards: {}, acks: {}, meta: {}, warnings: [] };
88
+ for (var i = 0; i < list.length; i += 1) {
89
+ var snapshot = list[i];
90
+ if (!snapshot) continue;
91
+ merged.messages = merged.messages.concat(snapshot.messages || []);
92
+ merged.warnings = merged.warnings.concat(snapshot.warnings || []);
93
+ copyInto(merged.agents, snapshot.agents);
94
+ copyInto(merged.cards, snapshot.cards);
95
+ copyInto(merged.acks, snapshot.acks);
96
+ if (snapshot.meta) {
97
+ for (var key in snapshot.meta) {
98
+ if (Object.prototype.hasOwnProperty.call(snapshot.meta, key)) merged.meta[key] = snapshot.meta[key];
99
+ }
100
+ }
101
+ }
102
+ merged.messages = CORE.dedupeMessages(merged.messages);
103
+ if (!merged.meta.generatedAt) merged.meta.generatedAt = new Date().toISOString();
104
+ merged.meta.source = "files";
105
+ return merged;
106
+ }
107
+
108
+ function copyInto(target, source) {
109
+ if (!source) return;
110
+ for (var key in source) {
111
+ if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
112
+ }
113
+ }
114
+
115
+ function readText(file) {
116
+ if (file && typeof file.text === "function") return file.text();
117
+ return new Promise(function (resolve, reject) {
118
+ var reader = new FileReader();
119
+ reader.onload = function () {
120
+ resolve(String(reader.result || ""));
121
+ };
122
+ reader.onerror = function () {
123
+ reject(reader.error || new Error("no se pudo leer " + (file && file.name)));
124
+ };
125
+ reader.readAsText(file);
126
+ });
127
+ }
128
+
129
+ /**
130
+ * Lee archivos JSON soltados. Acepta snapshot completo, un mensaje suelto del
131
+ * bus o un array de mensajes (lo que uno copia de `.oficina/bus/peer/`).
132
+ * Devuelve `errors` por archivo en vez de abortar todo por uno roto.
133
+ */
134
+ function readFiles(fileList) {
135
+ var files = Array.prototype.slice.call(fileList || []);
136
+ var errors = [];
137
+ var reads = files.map(function (file) {
138
+ return readText(file)
139
+ .then(function (text) {
140
+ var payload = JSON.parse(text);
141
+ return CORE.readSnapshot(payload);
142
+ })
143
+ .catch(function (error) {
144
+ errors.push({ name: file.name, error: String(error && error.message ? error.message : error) });
145
+ return null;
146
+ });
147
+ });
148
+
149
+ return Promise.all(reads).then(function (snapshots) {
150
+ var merged = mergeSnapshots(snapshots.filter(Boolean));
151
+ return { snapshot: merged, errors: errors, fileCount: files.length };
152
+ });
153
+ }
154
+
155
+ root.DebateData = {
156
+ fetchJson: fetchJson,
157
+ detectMode: detectMode,
158
+ loadFromServer: loadFromServer,
159
+ loadFromUrl: loadFromUrl,
160
+ readFiles: readFiles,
161
+ mergeSnapshots: mergeSnapshots,
162
+ };
163
+ })(typeof globalThis !== "undefined" ? globalThis : this);
@@ -0,0 +1,110 @@
1
+ /*
2
+ * debate-demo.js — snapshot sintético para ver el panel cuando no hay server
3
+ * ni acceso a .oficina/ (file://). Se rotula como DEMO en la UI: no son datos
4
+ * reales de la corrida. Incluye dos mensajes de ruido (un HELP_REQUEST a un
5
+ * solo agente y un STATUS broadcast) que el panel debe ignorar.
6
+ */
7
+ (function (root) {
8
+ "use strict";
9
+
10
+ function iso(secondsAgo) {
11
+ return new Date(Date.now() - secondsAgo * 1000).toISOString();
12
+ }
13
+
14
+ var now = Date.now();
15
+
16
+ var messages = [
17
+ {
18
+ id: "demo-r1", seq: 1, from: "e-mesa-visual", to: ["*"], kind: "HELP_REQUEST", subject: "T-03",
19
+ body: { need: "El panel de mesa no pinta el estado 'no-heartbeat' cuando el agente no latió nunca.", why_me: "mi card no toca la lógica de heartbeat, es de src/core" },
20
+ replyTo: null, inReplyTo: null, ttlSec: 900, createdAt: iso(240), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
21
+ },
22
+ {
23
+ id: "demo-r2", seq: 2, from: "e-bus-threading", to: ["*"], kind: "HELP_REQUEST", subject: "T-04",
24
+ body: { need: "Necesito decidir si los hilos se agrupan por inReplyTo o por subject cuando el padre venció por TTL.", why_me: "es una decisión de contrato de datos, no de mi UI" },
25
+ replyTo: null, inReplyTo: null, ttlSec: 900, createdAt: iso(150), priority: "high", idempotencyKey: null, hops: 0, channel: "peer",
26
+ },
27
+ {
28
+ id: "demo-a1", seq: 3, from: "e-testing-api-tester", to: ["e-mesa-visual"], kind: "HELP_ACCEPT", subject: "T-03",
29
+ body: { answer: "Mirá src/core/heartbeat.ts: el estado null se mapea a 'dead', no a 'no-heartbeat'. Lo correcto es no-heartbeat y tolerarlo en la UI.", summary: "Tomo el pedido" },
30
+ replyTo: null, inReplyTo: "demo-r1", ttlSec: 900, createdAt: iso(200), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
31
+ },
32
+ {
33
+ id: "demo-q1", seq: 4, from: "e-mesa-visual", to: ["e-testing-api-tester"], kind: "HELP_QUESTION", subject: "T-03",
34
+ body: { text: "¿El roster expone lastHeartbeat en ISO o en epoch? Si es epoch tengo que parsear distinto.", need: "confirmar formato de lastHeartbeat" },
35
+ replyTo: null, inReplyTo: "demo-a1", ttlSec: 900, createdAt: iso(160), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
36
+ },
37
+ {
38
+ id: "demo-a2", seq: 5, from: "e-testing-api-tester", to: ["e-mesa-visual"], kind: "HELP_ACCEPT", subject: "T-03",
39
+ body: { answer: "ISO 8601 con Z. Si viene null, el agente nunca latió: no-heartbeat.", detail: "Verificado contra roster.json de la corrida actual." },
40
+ replyTo: null, inReplyTo: "demo-q1", ttlSec: 900, createdAt: iso(120), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
41
+ },
42
+ {
43
+ id: "demo-s1", seq: 6, from: "e-mesa-visual", to: ["*"], kind: "STATUS", subject: "T-03",
44
+ body: { summary: "Resuelto con la respuesta de e-testing-api-tester. Queda pendiente el pulse animation.", state: "in_progress" },
45
+ replyTo: null, inReplyTo: "demo-r1", ttlSec: 900, createdAt: iso(90), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
46
+ },
47
+ {
48
+ id: "demo-d1", seq: 7, from: "e-arch", to: ["e-bus-threading"], kind: "HELP_DECLINE", subject: "T-04",
49
+ body: { reason: "No puedo decidir contratos de datos: eso es del Architect. Escalá por coord-arch.", why: "fuera de mi territorio" },
50
+ replyTo: null, inReplyTo: "demo-r2", ttlSec: 900, createdAt: iso(130), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
51
+ },
52
+ {
53
+ id: "demo-r3", seq: 8, from: "e-gates-panel", to: ["*"], kind: "HELP_REQUEST", subject: "T-09",
54
+ body: { need: "El gate 7 me devuelve criterios sin índice; no puedo linkear la evidencia al criterio correcto.", why_me: "mi may_touch es dashboard/**, no puedo tocar el motor de gates" },
55
+ replyTo: null, inReplyTo: null, ttlSec: 900, createdAt: iso(300), priority: "critical", idempotencyKey: null, hops: 0, channel: "peer",
56
+ },
57
+ {
58
+ id: "demo-p1", seq: 9, from: "e-testing-api-tester", to: ["e-gates-panel"], kind: "HELP_PARTIAL", subject: "T-09",
59
+ body: { answer: "Puedo darte el shape de acceptance indexado, pero el fix va en src/core/evidence.ts y eso no lo toco.", detail: "Te paso un fixture con el índice explícito para que testees el panel." },
60
+ replyTo: null, inReplyTo: "demo-r3", ttlSec: 900, createdAt: iso(280), priority: "critical", idempotencyKey: null, hops: 0, channel: "peer",
61
+ },
62
+ {
63
+ id: "demo-s2", seq: 10, from: "e-gates-panel", to: ["*"], kind: "STATUS", subject: "T-09",
64
+ body: { summary: "Cierro con el fixture parcial: el link por índice funciona cuando viene, si no cae al texto.", state: "done" },
65
+ replyTo: null, inReplyTo: "demo-r3", ttlSec: 900, createdAt: iso(270), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
66
+ },
67
+ {
68
+ id: "demo-noise-1", seq: 11, from: "e-mesa-visual", to: ["e-testing-api-tester"], kind: "HELP_REQUEST", subject: "T-03",
69
+ body: { need: "Pedido directo a un solo peer: no es debate, no debe abrir hilo." },
70
+ replyTo: null, inReplyTo: null, ttlSec: 900, createdAt: iso(100), priority: "low", idempotencyKey: null, hops: 0, channel: "peer",
71
+ },
72
+ {
73
+ id: "demo-noise-2", seq: 12, from: "oficina-director", to: ["*"], kind: "STATUS", subject: "T-00",
74
+ body: { summary: "Broadcast de estado sin pedido de ayuda: no abre debate." },
75
+ replyTo: null, inReplyTo: null, ttlSec: 900, createdAt: iso(80), priority: "normal", idempotencyKey: null, hops: 0, channel: "peer",
76
+ },
77
+ ];
78
+
79
+ var agents = {
80
+ "e-mesa-visual": { id: "e-mesa-visual", displayName: "Mesa Visual", role: "frontend-developer", state: "working" },
81
+ "e-bus-threading": { id: "e-bus-threading", displayName: "Bus Threading", role: "frontend-developer", state: "working" },
82
+ "e-testing-api-tester": { id: "e-testing-api-tester", displayName: "API Tester", role: "testing-api-tester", state: "idle_available" },
83
+ "e-gates-panel": { id: "e-gates-panel", displayName: "Gates Panel", role: "frontend-developer", state: "working" },
84
+ "e-arch": { id: "e-arch", displayName: "Architect", role: "architect", state: "idle_available" },
85
+ "oficina-director": { id: "oficina-director", displayName: "Director", role: "director", state: "working" },
86
+ };
87
+
88
+ var cards = {
89
+ "T-03": { id: "T-03", title: "Mesa visual en vivo", state: "in_progress" },
90
+ "T-04": { id: "T-04", title: "Bus de mensajes con threading", state: "in_progress" },
91
+ "T-09": { id: "T-09", title: "Panel de gates y evidencia", state: "in_progress" },
92
+ "T-00": { id: "T-00", title: "Guard anti-gate-verde-vacío", state: "done" },
93
+ };
94
+
95
+ root.DebateDemo = function demoSnapshot() {
96
+ return {
97
+ ok: true,
98
+ demo: true,
99
+ generatedAt: new Date(now).toISOString(),
100
+ messages: messages.slice(),
101
+ agents: agents,
102
+ cards: cards,
103
+ acks: {
104
+ "demo-r1": [{ agentId: "e-testing-api-tester", ackedAt: iso(210) }, { agentId: "e-gates-panel", ackedAt: iso(205) }],
105
+ "demo-r2": [{ agentId: "e-arch", ackedAt: iso(140) }],
106
+ },
107
+ warnings: [],
108
+ };
109
+ };
110
+ })(typeof globalThis !== "undefined" ? globalThis : this);