oficina-opencode 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +145 -0
- package/LICENSE +21 -0
- package/README.md +670 -2
- package/agents/oficina-architect.md +72 -0
- package/agents/oficina-commander.md +108 -0
- package/agents/oficina-critico.md +148 -0
- package/agents/oficina-director.md +160 -0
- package/agents/oficina-guardian.md +128 -0
- package/agents/oficina-integrator.md +98 -0
- package/agents/oficina-verificador.md +88 -0
- package/commands/oficina.md +36 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +394 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/briefing.d.ts +115 -0
- package/dist/core/briefing.d.ts.map +1 -0
- package/dist/core/briefing.js +410 -0
- package/dist/core/briefing.js.map +1 -0
- package/dist/core/bus.d.ts +132 -0
- package/dist/core/bus.d.ts.map +1 -0
- package/dist/core/bus.js +250 -0
- package/dist/core/bus.js.map +1 -0
- package/dist/core/catalog.d.ts +303 -0
- package/dist/core/catalog.d.ts.map +1 -0
- package/dist/core/catalog.js +816 -0
- package/dist/core/catalog.js.map +1 -0
- package/dist/core/dispatch.d.ts +110 -0
- package/dist/core/dispatch.d.ts.map +1 -0
- package/dist/core/dispatch.js +301 -0
- package/dist/core/dispatch.js.map +1 -0
- package/dist/core/factory.d.ts +74 -0
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +243 -0
- package/dist/core/factory.js.map +1 -0
- package/dist/core/fsx.d.ts +39 -0
- package/dist/core/fsx.d.ts.map +1 -0
- package/dist/core/fsx.js +113 -0
- package/dist/core/fsx.js.map +1 -0
- package/dist/core/gates.d.ts +97 -0
- package/dist/core/gates.d.ts.map +1 -0
- package/dist/core/gates.js +467 -0
- package/dist/core/gates.js.map +1 -0
- package/dist/core/leases.d.ts +46 -0
- package/dist/core/leases.d.ts.map +1 -0
- package/dist/core/leases.js +94 -0
- package/dist/core/leases.js.map +1 -0
- package/dist/core/log.d.ts +30 -0
- package/dist/core/log.d.ts.map +1 -0
- package/dist/core/log.js +43 -0
- package/dist/core/log.js.map +1 -0
- package/dist/core/mesa.d.ts +82 -0
- package/dist/core/mesa.d.ts.map +1 -0
- package/dist/core/mesa.js +178 -0
- package/dist/core/mesa.js.map +1 -0
- package/dist/core/office.d.ts +208 -0
- package/dist/core/office.d.ts.map +1 -0
- package/dist/core/office.js +618 -0
- package/dist/core/office.js.map +1 -0
- package/dist/core/paths.d.ts +67 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +136 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/retry.d.ts +41 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +86 -0
- package/dist/core/retry.js.map +1 -0
- package/dist/core/roster.d.ts +47 -0
- package/dist/core/roster.d.ts.map +1 -0
- package/dist/core/roster.js +146 -0
- package/dist/core/roster.js.map +1 -0
- package/dist/core/router.d.ts +67 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +186 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/state.d.ts +89 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +303 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/types.d.ts +333 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +27 -0
- package/dist/core/types.js.map +1 -0
- package/dist/hooks/compacting.d.ts +18 -0
- package/dist/hooks/compacting.d.ts.map +1 -0
- package/dist/hooks/compacting.js +72 -0
- package/dist/hooks/compacting.js.map +1 -0
- package/dist/hooks/scopeGuard.d.ts +39 -0
- package/dist/hooks/scopeGuard.d.ts.map +1 -0
- package/dist/hooks/scopeGuard.js +114 -0
- package/dist/hooks/scopeGuard.js.map +1 -0
- package/dist/hooks/sessionIdle.d.ts +35 -0
- package/dist/hooks/sessionIdle.d.ts.map +1 -0
- package/dist/hooks/sessionIdle.js +110 -0
- package/dist/hooks/sessionIdle.js.map +1 -0
- package/dist/hooks/systemTransform.d.ts +18 -0
- package/dist/hooks/systemTransform.d.ts.map +1 -0
- package/dist/hooks/systemTransform.js +63 -0
- package/dist/hooks/systemTransform.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/index.d.ts +22 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +854 -0
- package/dist/tools/index.js.map +1 -0
- package/docs/ARCHITECTURE.md +258 -0
- package/docs/QUE-ES.md +337 -0
- package/examples/opencode.json +7 -0
- package/package.json +81 -4
- package/skills/oficina/README.md +120 -0
- package/skills/oficina/SKILL.md +251 -0
- package/skills/oficina/reference/mesh.md +345 -0
- package/skills/oficina/reference/protocol.md +342 -0
- package/skills/oficina/reference/roles.md +171 -0
- package/skills/oficina/reference/templates.md +278 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bus de mensajes append-only sobre disco.
|
|
3
|
+
*
|
|
4
|
+
* Por qué disco y no memoria: los subagentes de OpenCode son procesos
|
|
5
|
+
* aislados. No comparten heap. Un bus en memoria sería un bus de una sola
|
|
6
|
+
* sesión. El disco es el único bus que sobrevive, que es compartible entre
|
|
7
|
+
* harnesses, y que es auditable después.
|
|
8
|
+
*
|
|
9
|
+
* Semántica:
|
|
10
|
+
* - Append-only: los mensajes nunca se borran. `seq` es monótono por canal.
|
|
11
|
+
* - Escritura atómica: tmp + rename. Nunca hay un JSON partido en el read.
|
|
12
|
+
* - Polling, no push: cada agente globa su inbox. Compatible con subagentes.
|
|
13
|
+
* - Ack: el receptor deja rastro de qué leyó.
|
|
14
|
+
* - TTL: un mensaje vencido es invisible para lectores nuevos, sigue en el log.
|
|
15
|
+
* - Idempotency key: un pedido con la misma key no se duplica.
|
|
16
|
+
* - Broadcast: `to: ["*"]` llega al pool; el primero que toma el lease gana.
|
|
17
|
+
*/
|
|
18
|
+
import type { BusMessage, MessageKind, MessagePriority } from "./types.js";
|
|
19
|
+
export interface BusOptions {
|
|
20
|
+
ttlSec?: number;
|
|
21
|
+
clock?: () => number;
|
|
22
|
+
}
|
|
23
|
+
export interface SendInput {
|
|
24
|
+
from: string;
|
|
25
|
+
to: string[];
|
|
26
|
+
kind: MessageKind;
|
|
27
|
+
subject: string;
|
|
28
|
+
body?: Record<string, unknown>;
|
|
29
|
+
replyTo?: string | null;
|
|
30
|
+
inReplyTo?: string | null;
|
|
31
|
+
ttlSec?: number;
|
|
32
|
+
priority?: MessagePriority;
|
|
33
|
+
idempotencyKey?: string | null;
|
|
34
|
+
hops?: number;
|
|
35
|
+
}
|
|
36
|
+
/** Canales disponibles. Los coordinadores son canales más. */
|
|
37
|
+
export declare const CHANNELS: {
|
|
38
|
+
readonly command: "coord-command";
|
|
39
|
+
readonly arch: "coord-arch";
|
|
40
|
+
readonly flow: "coord-flow";
|
|
41
|
+
readonly peer: "peer";
|
|
42
|
+
};
|
|
43
|
+
export type ChannelName = (typeof CHANNELS)[keyof typeof CHANNELS] | string;
|
|
44
|
+
/**
|
|
45
|
+
* Ruta mínima que el bus necesita. Se define acá para no acoplar el bus a
|
|
46
|
+
* `paths.ts` completo y poder testearlo con un stub.
|
|
47
|
+
*/
|
|
48
|
+
export interface ResolvedPathsLike {
|
|
49
|
+
busDir: string;
|
|
50
|
+
inboxDir: string;
|
|
51
|
+
}
|
|
52
|
+
export declare class Bus {
|
|
53
|
+
private readonly paths;
|
|
54
|
+
private readonly ttlDefault;
|
|
55
|
+
private readonly clock;
|
|
56
|
+
constructor(paths: ResolvedPathsLike, options?: BusOptions);
|
|
57
|
+
private channelDir;
|
|
58
|
+
private inboxOf;
|
|
59
|
+
/**
|
|
60
|
+
* Próximo `seq` de un canal.
|
|
61
|
+
*
|
|
62
|
+
* Se deriva de los archivos existentes, no de un contador en memoria: así
|
|
63
|
+
* dos emisores concurrentes no pueden producir el mismo seq.
|
|
64
|
+
*/
|
|
65
|
+
private nextSeq;
|
|
66
|
+
/**
|
|
67
|
+
* Publica un mensaje.
|
|
68
|
+
*
|
|
69
|
+
* Si ya existe un mensaje vivo con la misma `idempotencyKey` en el canal,
|
|
70
|
+
* no se publica un duplicado: se devuelve el existente.
|
|
71
|
+
*/
|
|
72
|
+
send(channel: string, input: SendInput): Promise<BusMessage>;
|
|
73
|
+
/** Busca un mensaje vivo por su idempotency key en un canal. */
|
|
74
|
+
findByIdempotencyKey(channel: string, key: string): Promise<BusMessage | undefined>;
|
|
75
|
+
/** Lee todos los mensajes de un canal, ordenados por seq. */
|
|
76
|
+
readChannel(channel: string): Promise<BusMessage[]>;
|
|
77
|
+
/** ¿El mensaje venció? */
|
|
78
|
+
isExpired(message: BusMessage): boolean;
|
|
79
|
+
/** ¿El mensaje va dirigido a este agente (o es broadcast)? */
|
|
80
|
+
isAddressedTo(message: BusMessage, agentId: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Recibe los mensajes dirigidos a un agente que todavía no acknowledgió.
|
|
83
|
+
*
|
|
84
|
+
* Los mensajes expirados se omiten (siguen en el log para auditoría).
|
|
85
|
+
*/
|
|
86
|
+
receive(agentId: string, channels?: string[]): Promise<BusMessage[]>;
|
|
87
|
+
/** Ids que el agente ya acknowledgió. */
|
|
88
|
+
ackedIds(agentId: string): Promise<Set<string>>;
|
|
89
|
+
/** Marca un mensaje como leído. */
|
|
90
|
+
ack(agentId: string, messageId: string): Promise<void>;
|
|
91
|
+
/** Ack de varios mensajes de una. */
|
|
92
|
+
ackMany(agentId: string, messageIds: string[]): Promise<void>;
|
|
93
|
+
/** ¿Quién acknowledged este mensaje? */
|
|
94
|
+
ackedBy(messageId: string): Promise<string[]>;
|
|
95
|
+
/** Canal donde vive un mensaje dado. */
|
|
96
|
+
channelOf(messageId: string): Promise<string | undefined>;
|
|
97
|
+
/**
|
|
98
|
+
* Publica un pedido de ayuda.
|
|
99
|
+
*
|
|
100
|
+
* La idempotency key deriva de card + agente + motivo, así dos intentos del
|
|
101
|
+
* mismo pedido no generan un storm en el pool.
|
|
102
|
+
*/
|
|
103
|
+
requestHelp(input: {
|
|
104
|
+
from: string;
|
|
105
|
+
to: string[];
|
|
106
|
+
cardId: string;
|
|
107
|
+
need: string;
|
|
108
|
+
whyMe: string;
|
|
109
|
+
hops?: number;
|
|
110
|
+
}): Promise<BusMessage>;
|
|
111
|
+
/** Responde un pedido de ayuda. */
|
|
112
|
+
respondHelp(input: {
|
|
113
|
+
from: string;
|
|
114
|
+
to: string[];
|
|
115
|
+
cardId: string;
|
|
116
|
+
kind: "HELP_ACCEPT" | "HELP_DECLINE" | "HELP_PARTIAL" | "HELP_QUESTION";
|
|
117
|
+
body: Record<string, unknown>;
|
|
118
|
+
inReplyTo: string;
|
|
119
|
+
hops?: number;
|
|
120
|
+
}): Promise<BusMessage>;
|
|
121
|
+
/** Broadcast al pool: "necesito X". El primero que acepta toma el lease. */
|
|
122
|
+
broadcastHelp(input: {
|
|
123
|
+
from: string;
|
|
124
|
+
cardId: string;
|
|
125
|
+
need: string;
|
|
126
|
+
whyMe: string;
|
|
127
|
+
hops?: number;
|
|
128
|
+
}): Promise<BusMessage>;
|
|
129
|
+
/** Snapshot de auditoría del bus. */
|
|
130
|
+
stats(): Promise<Record<string, number>>;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/core/bus.ts"],"names":[],"mappings":"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"}
|
package/dist/core/bus.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bus de mensajes append-only sobre disco.
|
|
3
|
+
*
|
|
4
|
+
* Por qué disco y no memoria: los subagentes de OpenCode son procesos
|
|
5
|
+
* aislados. No comparten heap. Un bus en memoria sería un bus de una sola
|
|
6
|
+
* sesión. El disco es el único bus que sobrevive, que es compartible entre
|
|
7
|
+
* harnesses, y que es auditable después.
|
|
8
|
+
*
|
|
9
|
+
* Semántica:
|
|
10
|
+
* - Append-only: los mensajes nunca se borran. `seq` es monótono por canal.
|
|
11
|
+
* - Escritura atómica: tmp + rename. Nunca hay un JSON partido en el read.
|
|
12
|
+
* - Polling, no push: cada agente globa su inbox. Compatible con subagentes.
|
|
13
|
+
* - Ack: el receptor deja rastro de qué leyó.
|
|
14
|
+
* - TTL: un mensaje vencido es invisible para lectores nuevos, sigue en el log.
|
|
15
|
+
* - Idempotency key: un pedido con la misma key no se duplica.
|
|
16
|
+
* - Broadcast: `to: ["*"]` llega al pool; el primero que toma el lease gana.
|
|
17
|
+
*/
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import fs from "node:fs/promises";
|
|
20
|
+
import { listFiles, nowIso, readJson, shortId, stableHash, writeJsonAtomic } from "./fsx.js";
|
|
21
|
+
/** Canales disponibles. Los coordinadores son canales más. */
|
|
22
|
+
export const CHANNELS = {
|
|
23
|
+
command: "coord-command",
|
|
24
|
+
arch: "coord-arch",
|
|
25
|
+
flow: "coord-flow",
|
|
26
|
+
peer: "peer",
|
|
27
|
+
};
|
|
28
|
+
export class Bus {
|
|
29
|
+
paths;
|
|
30
|
+
ttlDefault;
|
|
31
|
+
clock;
|
|
32
|
+
constructor(paths, options = {}) {
|
|
33
|
+
this.paths = paths;
|
|
34
|
+
this.ttlDefault = options.ttlSec ?? 900;
|
|
35
|
+
this.clock = options.clock ?? Date.now;
|
|
36
|
+
}
|
|
37
|
+
channelDir(channel) {
|
|
38
|
+
return path.join(this.paths.busDir, channel);
|
|
39
|
+
}
|
|
40
|
+
inboxOf(agentId) {
|
|
41
|
+
return path.join(this.paths.inboxDir, agentId);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Próximo `seq` de un canal.
|
|
45
|
+
*
|
|
46
|
+
* Se deriva de los archivos existentes, no de un contador en memoria: así
|
|
47
|
+
* dos emisores concurrentes no pueden producir el mismo seq.
|
|
48
|
+
*/
|
|
49
|
+
async nextSeq(channel) {
|
|
50
|
+
const files = await listFiles(this.channelDir(channel));
|
|
51
|
+
let max = 0;
|
|
52
|
+
for (const f of files) {
|
|
53
|
+
const m = /^(\d{4,})-/.exec(f);
|
|
54
|
+
if (m) {
|
|
55
|
+
const n = Number.parseInt(m[1], 10);
|
|
56
|
+
if (n > max)
|
|
57
|
+
max = n;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return max + 1;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Publica un mensaje.
|
|
64
|
+
*
|
|
65
|
+
* Si ya existe un mensaje vivo con la misma `idempotencyKey` en el canal,
|
|
66
|
+
* no se publica un duplicado: se devuelve el existente.
|
|
67
|
+
*/
|
|
68
|
+
async send(channel, input) {
|
|
69
|
+
if (input.idempotencyKey) {
|
|
70
|
+
const existing = await this.findByIdempotencyKey(channel, input.idempotencyKey);
|
|
71
|
+
if (existing)
|
|
72
|
+
return existing;
|
|
73
|
+
}
|
|
74
|
+
const seq = await this.nextSeq(channel);
|
|
75
|
+
const message = {
|
|
76
|
+
id: shortId("m"),
|
|
77
|
+
seq,
|
|
78
|
+
from: input.from,
|
|
79
|
+
to: input.to,
|
|
80
|
+
kind: input.kind,
|
|
81
|
+
subject: input.subject,
|
|
82
|
+
body: input.body ?? {},
|
|
83
|
+
replyTo: input.replyTo ?? null,
|
|
84
|
+
inReplyTo: input.inReplyTo ?? null,
|
|
85
|
+
ttlSec: input.ttlSec ?? this.ttlDefault,
|
|
86
|
+
createdAt: nowIso(this.clock),
|
|
87
|
+
priority: input.priority ?? "normal",
|
|
88
|
+
idempotencyKey: input.idempotencyKey ?? null,
|
|
89
|
+
hops: input.hops ?? 0,
|
|
90
|
+
};
|
|
91
|
+
const file = path.join(this.channelDir(channel), `${String(seq).padStart(4, "0")}-${message.id}.json`);
|
|
92
|
+
await writeJsonAtomic(file, message);
|
|
93
|
+
return message;
|
|
94
|
+
}
|
|
95
|
+
/** Busca un mensaje vivo por su idempotency key en un canal. */
|
|
96
|
+
async findByIdempotencyKey(channel, key) {
|
|
97
|
+
for (const message of await this.readChannel(channel)) {
|
|
98
|
+
if (message.idempotencyKey === key && !this.isExpired(message))
|
|
99
|
+
return message;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
/** Lee todos los mensajes de un canal, ordenados por seq. */
|
|
104
|
+
async readChannel(channel) {
|
|
105
|
+
const dir = this.channelDir(channel);
|
|
106
|
+
const files = (await listFiles(dir)).filter((f) => f.endsWith(".json")).sort();
|
|
107
|
+
const out = [];
|
|
108
|
+
for (const f of files) {
|
|
109
|
+
const msg = await readJson(path.join(dir, f));
|
|
110
|
+
if (msg)
|
|
111
|
+
out.push(msg);
|
|
112
|
+
}
|
|
113
|
+
return out.sort((a, b) => a.seq - b.seq);
|
|
114
|
+
}
|
|
115
|
+
/** ¿El mensaje venció? */
|
|
116
|
+
isExpired(message) {
|
|
117
|
+
const age = this.clock() - Date.parse(message.createdAt);
|
|
118
|
+
return age > message.ttlSec * 1000;
|
|
119
|
+
}
|
|
120
|
+
/** ¿El mensaje va dirigido a este agente (o es broadcast)? */
|
|
121
|
+
isAddressedTo(message, agentId) {
|
|
122
|
+
if (message.from === agentId)
|
|
123
|
+
return false;
|
|
124
|
+
if (message.to.includes("*"))
|
|
125
|
+
return true;
|
|
126
|
+
return message.to.includes(agentId);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Recibe los mensajes dirigidos a un agente que todavía no acknowledgió.
|
|
130
|
+
*
|
|
131
|
+
* Los mensajes expirados se omiten (siguen en el log para auditoría).
|
|
132
|
+
*/
|
|
133
|
+
async receive(agentId, channels = [CHANNELS.peer]) {
|
|
134
|
+
const acked = await this.ackedIds(agentId);
|
|
135
|
+
const out = [];
|
|
136
|
+
for (const channel of channels) {
|
|
137
|
+
for (const message of await this.readChannel(channel)) {
|
|
138
|
+
if (acked.has(message.id))
|
|
139
|
+
continue;
|
|
140
|
+
if (this.isExpired(message))
|
|
141
|
+
continue;
|
|
142
|
+
if (!this.isAddressedTo(message, agentId))
|
|
143
|
+
continue;
|
|
144
|
+
out.push(message);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return out.sort((a, b) => {
|
|
148
|
+
const prio = { critical: 0, high: 1, normal: 2, low: 3 };
|
|
149
|
+
const p = prio[a.priority] - prio[b.priority];
|
|
150
|
+
return p !== 0 ? p : a.seq - b.seq;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/** Ids que el agente ya acknowledgió. */
|
|
154
|
+
async ackedIds(agentId) {
|
|
155
|
+
const dir = this.inboxOf(agentId);
|
|
156
|
+
const files = await listFiles(dir);
|
|
157
|
+
const out = new Set();
|
|
158
|
+
for (const f of files) {
|
|
159
|
+
if (!f.endsWith(".ack"))
|
|
160
|
+
continue;
|
|
161
|
+
const ack = await readJson(path.join(dir, f));
|
|
162
|
+
if (ack)
|
|
163
|
+
out.add(ack.messageId);
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
/** Marca un mensaje como leído. */
|
|
168
|
+
async ack(agentId, messageId) {
|
|
169
|
+
const dir = this.inboxOf(agentId);
|
|
170
|
+
await fs.mkdir(dir, { recursive: true });
|
|
171
|
+
await writeJsonAtomic(path.join(dir, `${messageId}.ack`), {
|
|
172
|
+
messageId,
|
|
173
|
+
agentId,
|
|
174
|
+
ackedAt: nowIso(this.clock),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/** Ack de varios mensajes de una. */
|
|
178
|
+
async ackMany(agentId, messageIds) {
|
|
179
|
+
for (const id of messageIds)
|
|
180
|
+
await this.ack(agentId, id);
|
|
181
|
+
}
|
|
182
|
+
/** ¿Quién acknowledged este mensaje? */
|
|
183
|
+
async ackedBy(messageId) {
|
|
184
|
+
const dir = this.paths.inboxDir;
|
|
185
|
+
const agents = await listFiles(dir);
|
|
186
|
+
const out = [];
|
|
187
|
+
for (const agent of agents) {
|
|
188
|
+
const ack = await readJson(path.join(dir, agent, `${messageId}.ack`));
|
|
189
|
+
if (ack)
|
|
190
|
+
out.push(ack.agentId);
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
/** Canal donde vive un mensaje dado. */
|
|
195
|
+
async channelOf(messageId) {
|
|
196
|
+
const channels = await listFiles(this.paths.busDir);
|
|
197
|
+
for (const channel of channels) {
|
|
198
|
+
const files = await listFiles(path.join(this.paths.busDir, channel));
|
|
199
|
+
if (files.some((f) => f.includes(messageId)))
|
|
200
|
+
return channel;
|
|
201
|
+
}
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
205
|
+
// Protocolo de ayuda
|
|
206
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
207
|
+
/**
|
|
208
|
+
* Publica un pedido de ayuda.
|
|
209
|
+
*
|
|
210
|
+
* La idempotency key deriva de card + agente + motivo, así dos intentos del
|
|
211
|
+
* mismo pedido no generan un storm en el pool.
|
|
212
|
+
*/
|
|
213
|
+
async requestHelp(input) {
|
|
214
|
+
return this.send(CHANNELS.peer, {
|
|
215
|
+
from: input.from,
|
|
216
|
+
to: input.to,
|
|
217
|
+
kind: "HELP_REQUEST",
|
|
218
|
+
subject: input.cardId,
|
|
219
|
+
body: { need: input.need, why_me: input.whyMe },
|
|
220
|
+
idempotencyKey: `help:${input.cardId}:${input.from}:${stableHash(input.need)}`,
|
|
221
|
+
hops: input.hops ?? 0,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/** Responde un pedido de ayuda. */
|
|
225
|
+
async respondHelp(input) {
|
|
226
|
+
return this.send(CHANNELS.peer, {
|
|
227
|
+
from: input.from,
|
|
228
|
+
to: input.to,
|
|
229
|
+
kind: input.kind,
|
|
230
|
+
subject: input.cardId,
|
|
231
|
+
body: input.body,
|
|
232
|
+
inReplyTo: input.inReplyTo,
|
|
233
|
+
hops: input.hops ?? 0,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/** Broadcast al pool: "necesito X". El primero que acepta toma el lease. */
|
|
237
|
+
async broadcastHelp(input) {
|
|
238
|
+
return this.requestHelp({ ...input, to: ["*"] });
|
|
239
|
+
}
|
|
240
|
+
/** Snapshot de auditoría del bus. */
|
|
241
|
+
async stats() {
|
|
242
|
+
const channels = await listFiles(this.paths.busDir);
|
|
243
|
+
const out = {};
|
|
244
|
+
for (const channel of channels) {
|
|
245
|
+
out[channel] = (await this.readChannel(channel)).length;
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.js","sourceRoot":"","sources":["../../src/core/bus.ts"],"names":[],"mappings":"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"}
|