nexgenk 1.5.0 → 1.6.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 (2) hide show
  1. package/dist/main.mjs +1104 -826
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -47481,6 +47481,14 @@ var init_session$4 = __esmMin((() => {
47481
47481
  enabled: false
47482
47482
  });
47483
47483
  }
47484
+ async setWaveMode(enabled) {
47485
+ this.ensureOpen();
47486
+ if (typeof enabled !== "boolean") throw new NexgenError(ErrorCodes$1.REQUEST_INVALID, "Session wave mode must be a boolean");
47487
+ await this.rpc.setWaveMode({
47488
+ sessionId: this.id,
47489
+ enabled
47490
+ });
47491
+ }
47484
47492
  async setTowerMode(enabled, base) {
47485
47493
  this.ensureOpen();
47486
47494
  if (typeof enabled !== "boolean") throw new NexgenError(ErrorCodes$1.REQUEST_INVALID, "Session tower mode must be a boolean");
@@ -106094,6 +106102,12 @@ var init_statusReader = __esmMin((() => {
106094
106102
  INBOX_COUNT_LIMIT = 1e3;
106095
106103
  RECENT_LOG_LINES = 10;
106096
106104
  }));
106105
+ // ../../packages/agent-core-v2/src/features/wave/agent/waveMode.ts
106106
+ var IAgentWaveService;
106107
+ var init_waveMode = __esmMin((() => {
106108
+ init_instantiation();
106109
+ IAgentWaveService = createDecorator("agentWaveService");
106110
+ }));
106097
106111
  // ../../packages/agent-core-v2/src/_base/di/descriptors.ts
106098
106112
  var SyncDescriptor;
106099
106113
  var init_descriptors = __esmMin((() => {
@@ -125575,17 +125589,18 @@ var init_update_config = __esmMin((() => {
125575
125589
  };
125576
125590
  }));
125577
125591
  // ../../packages/agent-core-v2/src/features/wave/wave.ts
125578
- var WAVE_FLAG_ID, WAVE_FLAG_ENV, WAVE_SEAT_PROFILE, WAVE_TOOL_DOMAIN;
125592
+ var WAVE_FLAG_ID, WAVE_FLAG_ENV, WAVE_SEAT_PROFILE, WAVE_DIRECTOR_PROFILE, WAVE_TOOL_DOMAIN;
125579
125593
  var init_wave$3 = __esmMin((() => {
125580
125594
  WAVE_FLAG_ID = "wave";
125581
125595
  WAVE_FLAG_ENV = "NEXGEN_EXPERIMENTAL_WAVE";
125582
125596
  WAVE_SEAT_PROFILE = "wave-seat";
125597
+ WAVE_DIRECTOR_PROFILE = "agent";
125583
125598
  WAVE_TOOL_DOMAIN = "wave";
125584
125599
  }));
125585
125600
  // ../../packages/agent-core-v2/src/features/skill/catalog/builtin/wave.md?raw
125586
125601
  var wave_default;
125587
125602
  var init_wave$2 = __esmMin((() => {
125588
- wave_default = "---\nname: wave\ndescription: Run one design wave end to end — brief, three isolated proposals, defences and attacks, three judges with distinct biases, refinement with mandatory patches, the package gate, and task-by-task execution. Use when the user wants a backlog built and executed with adversarial review instead of a single agent's opinion.\ndisableModelInvocation: true\n---\n\n# Wave (diseño adversarial → backlog verificable)\n\nUna oleada convierte un brief en un backlog que se puede ejecutar y auditar. El valor no está en la prosa: está en que cada paso deja un artefacto con esquema y en que el gate puede salir en rojo.\n\n## Qué hace falta antes de empezar\n\n- `workflow.config.json` en la raíz (layout, umbrales, vocabulario, quórum, roles).\n- Un brief real: **siete secciones**, alcance con claves contables, evidencia disponible y rúbrica.\n\nSi falta el config, no hay oleada: pide al usuario que lo cree desde la plantilla. Si el brief tiene menos de siete secciones, `WaveRound` se niega a planificar — no lo rodees escribiendo el brief tú.\n\n## El ciclo\n\n1. `WaveOpen({ name })` crea `design/waves/W<n>-<slug>/` con el esqueleto del brief y declara la oleada en el config.\n2. Rellena el brief. Las preguntas de diseño van ahí, no en el prompt de un asiento.\n3. `WaveRound({ wave, round: \"propose\" })` devuelve el prompt de P1..Pn. **Lanza un agente por asiento** con `Agent` (o `AgentSwarm` si el usuario activó swarm), pasando el prompt tal cual. Cada asiento entrega con `WaveSubmit`.\n4. `WaveRound({ wave, round: \"defend\" })` y `round: \"attack\"` en la misma tanda.\n5. `WaveRound({ wave, round: \"judge\" })`. Cada juez devuelve su tabla y su total por propuesta.\n6. `WaveDecide({ wave, verdicts })` con los totales de los jueces: `adopt`, `refine` o `deadlock`. Sin `adopt`, lanza `refine` (R1) y después `audit` (R2).\n7. Aplica los parches de R2 y recompón el backlog (`consolidate` → `backlog`) por script, nunca a mano.\n8. `WaveGate({ wave })`: si sale en rojo, la oleada no está cerrada, por mucho que la prosa diga que sí.\n9. Ejecución: una task por vez; antes de cerrarla, `WaveVerify({ task })` con su `verify` y su mutante.\n\n## Reglas que no se negocian\n\n- **Un asiento, un artefacto.** `WaveSubmit` ata rol y ruta: un asiento no puede escribir el artefacto de otro ni tocar el backlog.\n- **Aislamiento en la ronda 1.** Un proponente no ve las propuestas de los otros. Si se ven, no son tres propuestas: es una con tres firmas.\n- **Los ataques no puntúan.** Un adversario que dice cuál es mejor está gastando su asiento; su producto son hallazgos con id y una mutación que los demuestra.\n- **Ningún hallazgo entra como párrafo.** Entra como override, task nueva o retirada, aplicado por script. Un defecto que acaba en el acta y no en el backlog no está corregido: está decorado.\n- **Quórum por mediana y por juez.** Un empate en la mediana no corona a nadie. Dos rondas sin quórum se cierran adoptando R1 **con disidencia escrita**, nunca presentándolo como unánime.\n\n## Qué no decir a un asiento\n\nNo le cuentes qué dicen las otras propuestas, no le pidas consenso, no le des el número de hallazgos esperado y no le adelantes qué propuesta quieres que gane. Cualquiera de las cuatro convierte el swarm en teatro.\n\n## Cuándo parar\n\n- Brief que depende de evidencia que no existe: declara `UNKNOWN` con dueño, o trocea la oleada. No se rellena con invención plausible.\n- Gate rojo por causa externa: regístralo como bloqueo con nombre y sigue con lo que no depende de ella. Nunca marques en verde lo que no se ejecutó.\n";
125603
+ wave_default = "---\nname: wave\ndescription: Run one design wave end to end as its director — open the wave, contract one agent per seat, then decide, verify and close on the gate. Use when the user enables wave mode and gives an objective for a backlog built with adversarial review instead of a single agent's opinion.\ndisableModelInvocation: true\n---\n\n# Wave (director → asientos → gate)\n\nEl agente principal es el **director**: abre la oleada, contrata asientos y comprueba resultados. **No escribe artefactos.** Cada artefacto lo entrega un asiento `wave-seat` con `WaveSubmit`, que es la única tool que hereda el rol, la ruta y el ticket de su contrato.\n\nUn asiento ve `Read`, `Glob`, `Grep` y `WaveSubmit` — nada más. No tiene gate, ni quórum, ni puede lanzar agentes: si necesita un dato que no tiene, escribe UNKNOWN y lo nombra.\n\n## Qué hace falta antes de empezar\n\n- `workflow.config.json` en la raíz (layout, umbrales, vocabulario, quórum, roles).\n- Un brief real: **siete secciones**, alcance con claves contables, evidencia disponible y rúbrica.\n\nSi falta el config, no hay oleada: pide al usuario que lo cree desde la plantilla. Si el brief tiene menos de siete secciones, `WaveContract` se niega — no lo rodees escribiendo el brief tú.\n\n## El ciclo\n\n1. `WaveOpen({ name })` crea `design/waves/W<n>-<slug>/` con el esqueleto del brief y declara la oleada en el config.\n2. Rellena el brief. Las preguntas de diseño van ahí, no en el prompt de un asiento.\n3. `WaveContract({ wave, round: \"propose\" })` devuelve un contrato por asiento: rol, ruta, ticket y prompt. **Lanza un agente `wave-seat` por contrato** con `Agent` (o `AgentSwarm` si el usuario activó swarm), pasando el prompt tal cual. No lo reescribas: el ticket del prompt es lo que `WaveSubmit` acepta.\n4. `round: \"defend\"` y `round: \"attack\"` en la misma tanda.\n5. `round: \"judge\"`: cada juez devuelve su tabla y su total por propuesta.\n6. `WaveDecide({ wave, verdicts })` con los totales de los jueces: `adopt`, `refine` o `deadlock`. Sin `adopt`, contrata `refine` (R1) y después `audit` (R2).\n7. Aplica los parches de R2 y recompón el backlog (`consolidate` → `backlog`) por script, nunca a mano.\n8. `WaveGate({ wave })`: si sale en rojo, la oleada no está cerrada, por mucho que la prosa diga que sí.\n9. Ejecución: una task por vez; antes de cerrarla, `WaveVerify({ task })` con su `verify` y su mutante.\n\n`WaveContract({ wave, round, report: true })` lista qué asientos ya entregaron y cuáles faltan, sin emitir tickets: úsalo para decidir si relanzas o cierras.\n\n## Reglas que no se negocian\n\n- **Un asiento, un artefacto, un contrato.** El contrato fija rol y ruta; `WaveSubmit` los exige. Un asiento no escribe el artefacto de otro ni toca el backlog, y el director no escribe ninguno.\n- **Aislamiento en la ronda 1.** Un proponente no ve las propuestas de los otros. Si se ven, no son tres propuestas: es una con tres firmas.\n- **Los ataques no puntúan.** Un adversario que dice cuál es mejor está gastando su asiento; su producto son hallazgos con id y una mutación que los demuestra.\n- **Ningún hallazgo entra como párrafo.** Entra como override, task nueva o retirada, aplicado por script. Un defecto que acaba en el acta y no en el backlog no está corregido: está decorado.\n- **Quórum por mediana y por juez.** Un empate en la mediana no corona a nadie. Dos rondas sin quórum se cierran adoptando R1 **con disidencia escrita**, nunca presentándolo como unánime.\n\n## Qué no decir a un asiento\n\nNo le cuentes qué dicen las otras propuestas, no le pidas consenso, no le des el número de hallazgos esperado y no le adelantes qué propuesta quieres que gane. Cualquiera de las cuatro convierte el swarm en teatro.\n\n## Cuándo parar\n\n- Brief que depende de evidencia que no existe: declara `UNKNOWN` con dueño, o trocea la oleada. No se rellena con invención plausible.\n- Gate rojo por causa externa: regístralo como bloqueo con nombre y sigue con lo que no depende de ella. Nunca marques en verde lo que no se ejecutó.\n";
125589
125604
  }));
125590
125605
  // ../../packages/agent-core-v2/src/features/skill/catalog/builtin/wave.ts
125591
125606
  var PSEUDO_PATH$1, parsed$1, WAVE_SKILL;
@@ -192175,7 +192190,7 @@ var init_host_request_headers = __esmMin((() => {
192175
192190
  }));
192176
192191
  // ../../packages/agent-core-v2/src/human/llm/requester/bases/anthropic/contract.ts
192177
192192
  var CONTEXT_MANAGEMENT_BETA;
192178
- var init_contract$3 = __esmMin((() => {
192193
+ var init_contract$5 = __esmMin((() => {
192179
192194
  CONTEXT_MANAGEMENT_BETA = "context-management-2025-06-27";
192180
192195
  }));
192181
192196
  // ../../packages/agent-core-v2/src/human/llm/syntax-errors.ts
@@ -192598,7 +192613,7 @@ function nexgenkUnsetCompletionTokens(input) {
192598
192613
  var NEXGEN_API_KEY_ENV, NEXGEN_BASE_URL_ENV$1, NEXGEN_DEFAULT_BASE_URL, nexgenkEndpoint$1, nexgenkConnection, nexgenkAcceptedImageMimes, nexgenkResponsesTrait, nexgenkOpenAITrait, nexgenkAnthropicTrait;
192599
192614
  var init_trait = __esmMin((() => {
192600
192615
  init_image_formats();
192601
- init_contract$3();
192616
+ init_contract$5();
192602
192617
  init_schema$3();
192603
192618
  NEXGEN_API_KEY_ENV = "NEXGEN_API_KEY";
192604
192619
  NEXGEN_BASE_URL_ENV$1 = "NEXGEN_BASE_URL";
@@ -196108,7 +196123,7 @@ var init_format$4 = __esmMin((() => {
196108
196123
  init_syntax_errors();
196109
196124
  init_patterns$3();
196110
196125
  init_rewrite();
196111
- init_contract$3();
196126
+ init_contract$5();
196112
196127
  init_lower$3();
196113
196128
  init_patterns$2();
196114
196129
  init_profile();
@@ -219258,6 +219273,90 @@ var init_towerService = __esmMin((() => {
219258
219273
  ], AgentTowerService);
219259
219274
  registerScopedService("agent", IAgentTowerService, AgentTowerService, 0, "tower");
219260
219275
  }));
219276
+ // ../../packages/agent-core-v2/src/features/wave/waveOps.ts
219277
+ var waveModeEnterSchema, WaveModeEnter, waveModeExitSchema, WaveModeExit, waveKey;
219278
+ var init_waveOps = __esmMin((() => {
219279
+ init_zod$1();
219280
+ init_usageEvents();
219281
+ init_event2();
219282
+ init_state$1();
219283
+ waveModeEnterSchema = object$1({ agentId: string() });
219284
+ WaveModeEnter = class extends AgentEvent2 {
219285
+ static type = "wave_mode.enter";
219286
+ static durable = true;
219287
+ static schema = waveModeEnterSchema;
219288
+ };
219289
+ waveModeExitSchema = object$1({ agentId: string() });
219290
+ WaveModeExit = class extends AgentEvent2 {
219291
+ static type = "wave_mode.exit";
219292
+ static durable = true;
219293
+ static schema = waveModeExitSchema;
219294
+ };
219295
+ waveKey = defineState("wave", () => false).replayable({ schema: boolean$1() }).on(WaveModeEnter, (_s, e, ctx) => {
219296
+ ctx.emit(new AgentStatusUpdated({
219297
+ agentId: e.agentId,
219298
+ waveMode: true
219299
+ }));
219300
+ return true;
219301
+ }).on(WaveModeExit, (_s, e, ctx) => {
219302
+ ctx.emit(new AgentStatusUpdated({
219303
+ agentId: e.agentId,
219304
+ waveMode: false
219305
+ }));
219306
+ return false;
219307
+ });
219308
+ }));
219309
+ // ../../packages/agent-core-v2/src/features/wave/agent/waveService.ts
219310
+ var AgentWaveService;
219311
+ var init_waveService = __esmMin((() => {
219312
+ init_lifecycle$2();
219313
+ init_profile$1();
219314
+ init_scopeContext();
219315
+ init_agentState();
219316
+ init_eventBus();
219317
+ init_agentLifecycle();
219318
+ init_eventDispatcher();
219319
+ init_waveOps();
219320
+ init_decorateParam();
219321
+ init_decorate();
219322
+ AgentWaveService = class AgentWaveService extends Disposable {
219323
+ dispatcher;
219324
+ agentCtx;
219325
+ agentState;
219326
+ profile;
219327
+ constructor(dispatcher, agentCtx, agentState, profile, eventBus) {
219328
+ super();
219329
+ this.dispatcher = dispatcher;
219330
+ this.agentCtx = agentCtx;
219331
+ this.agentState = agentState;
219332
+ this.profile = profile;
219333
+ this.agentState.contributeState(waveKey);
219334
+ const republish = () => {
219335
+ if (this.agentCtx.agentId === "main") this.profile.republishStatus();
219336
+ };
219337
+ this._register(eventBus.subscribe(WaveModeEnter, republish));
219338
+ this._register(eventBus.subscribe(WaveModeExit, republish));
219339
+ }
219340
+ async enter() {
219341
+ if (this.agentState.get(waveKey)) return;
219342
+ await this.dispatcher.dispatch(new WaveModeEnter({ agentId: this.agentCtx.agentId }));
219343
+ }
219344
+ async exit() {
219345
+ if (!this.agentState.get(waveKey)) return;
219346
+ await this.dispatcher.dispatch(new WaveModeExit({ agentId: this.agentCtx.agentId }));
219347
+ }
219348
+ get isActive() {
219349
+ return this.agentState.get(waveKey);
219350
+ }
219351
+ };
219352
+ AgentWaveService = __decorate([
219353
+ __decorateParam(0, IEventDispatcher),
219354
+ __decorateParam(1, IAgentScopeContext),
219355
+ __decorateParam(2, IAgentStateService),
219356
+ __decorateParam(3, IAgentProfileService),
219357
+ __decorateParam(4, IEventBus)
219358
+ ], AgentWaveService);
219359
+ }));
219261
219360
  // ../../packages/agent-core-v2/src/features/wave/seatProfile.ts
219262
219361
  var WAVE_SEAT_TOOLS, WAVE_SEAT_ROLE, WAVE_SEAT_PROFILE_DEF;
219263
219362
  var init_seatProfile = __esmMin((() => {
@@ -219279,243 +219378,67 @@ var init_seatProfile = __esmMin((() => {
219279
219378
  renderSystemPrompt: (context) => renderSystemPromptResult(WAVE_SEAT_ROLE, context, { skillActive: skillActiveFor(WAVE_SEAT_TOOLS) })
219280
219379
  });
219281
219380
  }));
219282
- // ../../packages/agent-core-v2/src/features/wave/tools/decide/decide.ts
219283
- var WaveDecideToolInputSchema, IWaveDecideTool;
219284
- var init_decide$1 = __esmMin((() => {
219381
+ // ../../packages/agent-core-v2/src/features/wave/tools/contract/contract.ts
219382
+ var WAVE_CONTRACT_ROUNDS, WaveContractToolInputSchema, IWaveContractTool;
219383
+ var init_contract$4 = __esmMin((() => {
219285
219384
  init_zod$1();
219286
219385
  init_instantiation();
219287
- WaveDecideToolInputSchema = object$1({
219288
- root: string().optional(),
219289
- verdicts: array(object$1({
219290
- judge: string().min(1),
219291
- scores: record$3(string(), number$1())
219292
- }).strict()).min(1),
219293
- minPoints: number$1().optional(),
219294
- minAxes: number$1().optional(),
219295
- round: number$1().optional()
219386
+ WAVE_CONTRACT_ROUNDS = [
219387
+ "propose",
219388
+ "defend",
219389
+ "attack",
219390
+ "judge",
219391
+ "refine",
219392
+ "audit"
219393
+ ];
219394
+ WaveContractToolInputSchema = object$1({
219395
+ wave: string().min(2),
219396
+ round: _enum(WAVE_CONTRACT_ROUNDS),
219397
+ seats: array(object$1({
219398
+ role: string().min(1).optional(),
219399
+ artifact: string().min(1).optional()
219400
+ }).strict()).optional(),
219401
+ report: boolean$1().optional(),
219402
+ root: string().optional()
219296
219403
  }).strict();
219297
- IWaveDecideTool = createDecorator("waveDecideTool");
219404
+ IWaveContractTool = createDecorator("waveContractTool");
219298
219405
  }));
219299
- // ../../packages/agent-core-v2/src/features/wave/core/config.ts
219300
- function seats(prefix, count) {
219301
- return Array.from({ length: Math.max(0, count) }, (_, index) => `${prefix}${index + 1}`);
219302
- }
219303
- function waveRoundSeats(roles, round) {
219304
- if (round === "propose") return seats("P", roles.proponents);
219305
- if (round === "defend") return seats("D", roles.defenders);
219306
- if (round === "attack") return seats("A", roles.adversaries);
219307
- if (round === "judge") return seats("J", roles.judges);
219308
- const refiners = seats("R", roles.refiners);
219309
- return round === "refine" ? refiners.slice(0, 1) : refiners.slice(1);
219310
- }
219311
- function waveConfigState(config) {
219312
- const state = config;
219313
- return {
219314
- ...config,
219315
- version: state.version ?? 1,
219316
- wavesOpen: state.wavesOpen ?? [],
219317
- raw: state.raw ?? {}
219318
- };
219406
+ // ../../packages/agent-core-v2/src/features/wave/core/fsJson.ts
219407
+ function readJson$2(fs, path) {
219408
+ const text = fs.read(path);
219409
+ if (text === void 0) return void 0;
219410
+ try {
219411
+ return JSON.parse(text);
219412
+ } catch {
219413
+ return;
219414
+ }
219319
219415
  }
219320
- function record$2(value) {
219321
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
219416
+ function isRecord$16(value) {
219417
+ return typeof value === "object" && value !== null && !Array.isArray(value);
219322
219418
  }
219323
- function num(value, fallback) {
219324
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
219419
+ function asRecord$2(value) {
219420
+ return isRecord$16(value) ? value : {};
219325
219421
  }
219326
- function text(value, fallback) {
219327
- return typeof value === "string" ? value : fallback;
219422
+ function asArray$2(value) {
219423
+ return Array.isArray(value) ? value : [];
219328
219424
  }
219329
- function strArray$1(value, fallback) {
219330
- return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : fallback;
219425
+ function asText$1(value) {
219426
+ return typeof value === "string" ? value : "";
219331
219427
  }
219332
- function makeWaveConfig(raw, loadedFrom) {
219333
- const base = record$2(raw);
219334
- const layout = record$2(base["layout"]);
219335
- const thresholds = record$2(base["thresholds"]);
219336
- const vocabulary = record$2(base["vocabulary"]);
219337
- const roles = record$2(base["roles"]);
219338
- const quorum = record$2(base["quorum"]);
219339
- return {
219340
- version: num(base["version"], 1),
219341
- wavesOpen: strArray$1(base["wavesOpen"], []),
219342
- raw: base,
219343
- layout: {
219344
- waves: text(layout["waves"], DEFAULT_LAYOUT.waves),
219345
- backlog: text(layout["backlog"], DEFAULT_LAYOUT.backlog),
219346
- adr: text(layout["adr"], DEFAULT_LAYOUT.adr),
219347
- inventory: text(layout["inventory"], DEFAULT_LAYOUT.inventory)
219348
- },
219349
- waves: strArray$1(base["waves"], ["W0"]),
219350
- thresholds: {
219351
- minEpics: num(thresholds["minEpics"], DEFAULT_THRESHOLDS.minEpics),
219352
- minTasks: num(thresholds["minTasks"], DEFAULT_THRESHOLDS.minTasks),
219353
- minAcPerTask: num(thresholds["minAcPerTask"], DEFAULT_THRESHOLDS.minAcPerTask),
219354
- minDecisionBytes: num(thresholds["minDecisionBytes"], DEFAULT_THRESHOLDS.minDecisionBytes),
219355
- minArtifactBytes: num(thresholds["minArtifactBytes"], DEFAULT_THRESHOLDS.minArtifactBytes),
219356
- minBriefSections: num(thresholds["minBriefSections"], DEFAULT_THRESHOLDS.minBriefSections),
219357
- minContextBytes: num(thresholds["minContextBytes"], DEFAULT_THRESHOLDS.minContextBytes),
219358
- minGoalChars: num(thresholds["minGoalChars"], DEFAULT_THRESHOLDS.minGoalChars),
219359
- minScopeChars: num(thresholds["minScopeChars"], DEFAULT_THRESHOLDS.minScopeChars),
219360
- minFieldChars: num(thresholds["minFieldChars"], DEFAULT_THRESHOLDS.minFieldChars),
219361
- minMetricChars: num(thresholds["minMetricChars"], DEFAULT_THRESHOLDS.minMetricChars)
219362
- },
219363
- vocabulary: {
219364
- areas: strArray$1(vocabulary["areas"], DEFAULT_VOCABULARY.areas),
219365
- requiredAreas: strArray$1(vocabulary["requiredAreas"], DEFAULT_VOCABULARY.requiredAreas),
219366
- types: strArray$1(vocabulary["types"], DEFAULT_VOCABULARY.types),
219367
- estimates: strArray$1(vocabulary["estimates"], DEFAULT_VOCABULARY.estimates),
219368
- vagueWords: strArray$1(vocabulary["vagueWords"], DEFAULT_VOCABULARY.vagueWords),
219369
- verifyCommands: strArray$1(vocabulary["verifyCommands"], DEFAULT_VOCABULARY.verifyCommands),
219370
- verifyPrefixes: strArray$1(vocabulary["verifyPrefixes"], DEFAULT_VOCABULARY.verifyPrefixes)
219371
- },
219372
- secrets: strArray$1(base["secrets"], []),
219373
- roles: {
219374
- proponents: num(roles["proponents"], DEFAULT_ROLES.proponents),
219375
- defenders: num(roles["defenders"], DEFAULT_ROLES.defenders),
219376
- adversaries: num(roles["adversaries"], DEFAULT_ROLES.adversaries),
219377
- judges: num(roles["judges"], DEFAULT_ROLES.judges),
219378
- refiners: num(roles["refiners"], DEFAULT_ROLES.refiners)
219379
- },
219380
- quorum: {
219381
- minPoints: num(quorum["minPoints"], DEFAULT_QUORUM.minPoints),
219382
- minAxes: num(quorum["minAxes"], DEFAULT_QUORUM.minAxes),
219383
- axes: strArray$1(quorum["axes"], DEFAULT_QUORUM.axes)
219384
- },
219385
- loadedFrom
219386
- };
219428
+ function writeJson(_fs, path, value) {
219429
+ mkdirSync(dirname(path), { recursive: true });
219430
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
219387
219431
  }
219388
- function readWaveConfig(fs, root) {
219389
- const file = resolve(root, "workflow.config.json");
219390
- const raw = fs.read(file);
219391
- if (raw === void 0) return void 0;
219392
- try {
219393
- return makeWaveConfig(JSON.parse(raw), file);
219394
- } catch {
219395
- return;
219432
+ function walk$1(fs, dir, accept, acc = []) {
219433
+ if (!fs.isDirectory(dir)) return acc;
219434
+ for (const name of fs.list(dir)) {
219435
+ const full = join(dir, name);
219436
+ if (fs.isDirectory(full)) walk$1(fs, full, accept, acc);
219437
+ else if (accept(full)) acc.push(full);
219396
219438
  }
219439
+ return acc;
219397
219440
  }
219398
- function containedPath(root, candidate) {
219399
- const resolvedRoot = resolve(root);
219400
- const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(resolvedRoot, candidate);
219401
- if (resolved !== resolvedRoot && !resolved.startsWith(`${resolvedRoot}${sep}`)) throw new Error(`ruta fuera del workspace: ${candidate}`);
219402
- return resolved;
219403
- }
219404
- var WAVE_ROUNDS, WAVE_ROUND_DIRS, DEFAULT_LAYOUT, DEFAULT_THRESHOLDS, DEFAULT_VOCABULARY, DEFAULT_ROLES, DEFAULT_QUORUM;
219405
- var init_config$6 = __esmMin((() => {
219406
- WAVE_ROUNDS = [
219407
- "propose",
219408
- "defend",
219409
- "attack",
219410
- "judge",
219411
- "refine",
219412
- "audit"
219413
- ];
219414
- WAVE_ROUND_DIRS = {
219415
- propose: "01-proposals",
219416
- defend: "02-defenses",
219417
- attack: "03-attacks",
219418
- judge: "04-verdicts",
219419
- refine: "05-refined",
219420
- audit: "05-refined"
219421
- };
219422
- DEFAULT_LAYOUT = {
219423
- waves: "design/waves",
219424
- backlog: "backlog",
219425
- adr: "design/ADR",
219426
- inventory: "inventory"
219427
- };
219428
- DEFAULT_THRESHOLDS = {
219429
- minEpics: 1,
219430
- minTasks: 1,
219431
- minAcPerTask: 2,
219432
- minDecisionBytes: 200,
219433
- minArtifactBytes: 100,
219434
- minBriefSections: 7,
219435
- minContextBytes: 80,
219436
- minGoalChars: 40,
219437
- minScopeChars: 40,
219438
- minFieldChars: 10,
219439
- minMetricChars: 4
219440
- };
219441
- DEFAULT_VOCABULARY = {
219442
- areas: [
219443
- "cliente",
219444
- "seller",
219445
- "agent",
219446
- "admin",
219447
- "plataforma"
219448
- ],
219449
- requiredAreas: [],
219450
- types: [
219451
- "db",
219452
- "api",
219453
- "ui",
219454
- "domain",
219455
- "infra",
219456
- "test",
219457
- "docs",
219458
- "app",
219459
- "contract",
219460
- "ops",
219461
- "tooling"
219462
- ],
219463
- estimates: [
219464
- "XS",
219465
- "S",
219466
- "M",
219467
- "L",
219468
- "XL"
219469
- ],
219470
- vagueWords: [
219471
- "funciona",
219472
- "correcto",
219473
- "adecuado",
219474
- "rapido",
219475
- "robusto",
219476
- "bonito"
219477
- ],
219478
- verifyCommands: [
219479
- "pnpm",
219480
- "npm",
219481
- "node",
219482
- "bash",
219483
- "sh",
219484
- "make",
219485
- "python",
219486
- "go",
219487
- "cargo",
219488
- "dotnet"
219489
- ],
219490
- verifyPrefixes: [
219491
- "test/unit/",
219492
- "test/db/",
219493
- "test/domain/",
219494
- "test/api/",
219495
- "test/perf/",
219496
- "e2e/",
219497
- "scripts/checks/"
219498
- ]
219499
- };
219500
- DEFAULT_ROLES = {
219501
- proponents: 3,
219502
- defenders: 3,
219503
- adversaries: 3,
219504
- judges: 3,
219505
- refiners: 2
219506
- };
219507
- DEFAULT_QUORUM = {
219508
- minPoints: 1,
219509
- minAxes: 2,
219510
- axes: [
219511
- "cobertura",
219512
- "falsabilidad",
219513
- "riesgo",
219514
- "coste",
219515
- "encaje"
219516
- ]
219517
- };
219518
- }));
219441
+ var init_fsJson = __esmMin((() => {}));
219519
219442
  // ../../packages/agent-core-v2/src/features/wave/core/nodeFs.ts
219520
219443
  var nodeWaveFs;
219521
219444
  var init_nodeFs = __esmMin((() => {
@@ -219557,477 +219480,353 @@ var init_nodeFs = __esmMin((() => {
219557
219480
  }
219558
219481
  };
219559
219482
  }));
219560
- // ../../packages/agent-core-v2/src/features/wave/core/quorum.ts
219561
- function median(values) {
219562
- const sorted = [...values].toSorted((left, right) => left - right);
219563
- const middle = Math.floor(sorted.length / 2);
219564
- const current = sorted[middle];
219565
- if (current === void 0) throw new Error("no se puede calcular una mediana sin valores");
219566
- if (sorted.length % 2 === 1) return current;
219567
- const previous = sorted[middle - 1];
219568
- if (previous === void 0) throw new Error("no se puede calcular una mediana sin valores");
219569
- return (previous + current) / 2;
219483
+ // ../../packages/agent-core-v2/src/features/wave/core/config.ts
219484
+ function seats(prefix, count) {
219485
+ return Array.from({ length: Math.max(0, count) }, (_, index) => `${prefix}${index + 1}`);
219570
219486
  }
219571
- function validate$1(verdicts) {
219572
- if (verdicts.length === 0) throw new Error("el quorum exige al menos un veredicto");
219573
- const first = verdicts[0];
219574
- if (first === void 0) throw new Error("el quorum exige al menos un veredicto");
219575
- const proposals = Object.keys(first.scores).toSorted();
219576
- if (proposals.length === 0) throw new Error("el veredicto no puntua ninguna propuesta");
219577
- for (const verdict of verdicts) {
219578
- const keys = Object.keys(verdict.scores).toSorted();
219579
- if (keys.length !== proposals.length || keys.some((key, index) => key !== proposals[index])) throw new Error(`el juez ${verdict.judge} no puntua el mismo conjunto de propuestas`);
219580
- for (const proposal of proposals) {
219581
- const score = verdict.scores[proposal];
219582
- if (typeof score !== "number" || !Number.isFinite(score) || score < 0) throw new Error(`puntuacion invalida de ${verdict.judge} para ${proposal}`);
219583
- }
219584
- }
219585
- return proposals;
219487
+ function waveRoundSeats(roles, round) {
219488
+ if (round === "propose") return seats("P", roles.proponents);
219489
+ if (round === "defend") return seats("D", roles.defenders);
219490
+ if (round === "attack") return seats("A", roles.adversaries);
219491
+ if (round === "judge") return seats("J", roles.judges);
219492
+ const refiners = seats("R", roles.refiners);
219493
+ return round === "refine" ? refiners.slice(0, 1) : refiners.slice(1);
219586
219494
  }
219587
- function evaluateQuorum(input) {
219588
- const proposals = validate$1(input.verdicts);
219589
- const minPoints = input.minPoints;
219590
- const minAxes = Math.max(1, Math.floor(input.minAxes));
219591
- const round = input.round ?? 0;
219592
- const medians = {};
219593
- for (const proposal of proposals) medians[proposal] = median(input.verdicts.map((verdict) => verdict.scores[proposal] ?? 0));
219594
- const ranked = [...proposals].toSorted((left, right) => {
219595
- const delta = (medians[right] ?? 0) - (medians[left] ?? 0);
219596
- return delta === 0 ? left.localeCompare(right) : delta;
219597
- });
219598
- const winner = ranked[0];
219599
- const second = ranked[1];
219600
- if (winner === void 0) throw new Error("no hay propuestas que comparar");
219601
- if (second === void 0) return {
219602
- verdict: "refine",
219603
- winner,
219604
- second: void 0,
219605
- medians,
219606
- medianMargin: 0,
219607
- judgesHoldingMargin: 0,
219608
- perJudge: [],
219609
- reason: `sin comparacion no hay quorum: solo hay una propuesta (${winner})`
219610
- };
219611
- const winnerMedian = medians[winner] ?? 0;
219612
- const secondMedian = medians[second] ?? 0;
219613
- const medianMargin = Number((winnerMedian - secondMedian).toFixed(6));
219614
- if (medianMargin === 0) return {
219615
- verdict: round >= 2 ? "deadlock" : "refine",
219616
- winner: void 0,
219617
- second,
219618
- medians,
219619
- medianMargin,
219620
- judgesHoldingMargin: 0,
219621
- perJudge: [],
219622
- reason: `empate en la mediana entre ${winner} y ${second}: no hay ganador`
219623
- };
219624
- const perJudge = input.verdicts.map((verdict) => {
219625
- const margin = (verdict.scores[winner] ?? 0) - (verdict.scores[second] ?? 0);
219626
- return {
219627
- judge: verdict.judge,
219628
- margin,
219629
- holds: margin >= minPoints
219630
- };
219631
- });
219632
- const judgesHoldingMargin = perJudge.filter((entry) => entry.holds).length;
219633
- const marginOk = medianMargin >= minPoints;
219634
- const majorityOk = judgesHoldingMargin >= minAxes;
219635
- if (marginOk && majorityOk) return {
219636
- verdict: "adopt",
219637
- winner,
219638
- second,
219639
- medians,
219640
- medianMargin,
219641
- judgesHoldingMargin,
219642
- perJudge,
219643
- reason: `mediana ${winner} ${winnerMedian} vs ${second} ${secondMedian}: margen ${medianMargin} en ${judgesHoldingMargin}/${perJudge.length} jueces`
219644
- };
219645
- const reason = [marginOk ? void 0 : `margen ${medianMargin} < ${minPoints}`, majorityOk ? void 0 : `solo ${judgesHoldingMargin}/${perJudge.length} jueces superan el margen (minimo ${minAxes})`].filter((part) => part !== void 0).join(" y ");
219495
+ function waveConfigState(config) {
219496
+ const state = config;
219646
219497
  return {
219647
- verdict: round >= 2 ? "deadlock" : "refine",
219648
- winner,
219649
- second,
219650
- medians,
219651
- medianMargin,
219652
- judgesHoldingMargin,
219653
- perJudge,
219654
- reason: `${reason}: sin quorum`
219498
+ ...config,
219499
+ version: state.version ?? 1,
219500
+ wavesOpen: state.wavesOpen ?? [],
219501
+ raw: state.raw ?? {}
219655
219502
  };
219656
219503
  }
219657
- var init_quorum = __esmMin((() => {}));
219658
- // ../../packages/agent-core-v2/src/features/wave/tools/decide/decideRunner.ts
219659
- function runWaveDecideForRoot(input) {
219660
- const root = resolve(input.root);
219661
- const config = readWaveConfig(nodeWaveFs, root);
219662
- if (config === void 0) return {
219663
- output: `no hay workflow.config.json en ${root}`,
219664
- isError: true
219665
- };
219666
- let result;
219667
- try {
219668
- result = evaluateQuorum({
219669
- verdicts: input.verdicts,
219670
- minPoints: input.minPoints ?? config.quorum.minPoints,
219671
- minAxes: input.minAxes ?? config.quorum.minAxes,
219672
- round: input.round
219673
- });
219674
- } catch (error) {
219675
- return {
219676
- output: `veredictos invalidos: ${error instanceof Error ? error.message : String(error)}`,
219677
- isError: true
219678
- };
219679
- }
219680
- const holding = result.perJudge.length === 0 ? "sin comparacion" : `${result.judgesHoldingMargin}/${result.perJudge.length}`;
219681
- const lines = [
219682
- `QUORUM — ${result.verdict}`,
219683
- "",
219684
- ...Object.entries(result.medians).map(([proposal, value]) => ` mediana ${proposal}: ${value}`),
219685
- "",
219686
- ` ganador: ${result.winner ?? "(empate)"}`,
219687
- ` margen: ${result.medianMargin} (minimo ${input.minPoints ?? config.quorum.minPoints})`,
219688
- ` jueces con margen: ${holding} (minimo ${input.minAxes ?? config.quorum.minAxes})`,
219689
- ...result.perJudge.map((entry) => ` ${entry.judge}: ${entry.margin}${entry.holds ? " ok" : " NO"}`),
219690
- ...result.perJudge.filter((entry) => entry.margin < 0).map((entry) => ` disidencia: ${entry.judge} ${entry.margin}`),
219691
- "",
219692
- ` motivo: ${result.reason}`
219693
- ];
219694
- if (result.verdict === "deadlock") lines.push("", " dos rondas sin quorum: adopta R1 con disidencia escrita, nunca la presentes como unanime");
219504
+ function record$2(value) {
219505
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
219506
+ }
219507
+ function num(value, fallback) {
219508
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
219509
+ }
219510
+ function text(value, fallback) {
219511
+ return typeof value === "string" ? value : fallback;
219512
+ }
219513
+ function strArray$1(value, fallback) {
219514
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : fallback;
219515
+ }
219516
+ function makeWaveConfig(raw, loadedFrom) {
219517
+ const base = record$2(raw);
219518
+ const layout = record$2(base["layout"]);
219519
+ const thresholds = record$2(base["thresholds"]);
219520
+ const vocabulary = record$2(base["vocabulary"]);
219521
+ const roles = record$2(base["roles"]);
219522
+ const quorum = record$2(base["quorum"]);
219695
219523
  return {
219696
- output: lines.join("\n"),
219697
- isError: result.verdict !== "adopt"
219524
+ version: num(base["version"], 1),
219525
+ wavesOpen: strArray$1(base["wavesOpen"], []),
219526
+ raw: base,
219527
+ layout: {
219528
+ waves: text(layout["waves"], DEFAULT_LAYOUT.waves),
219529
+ backlog: text(layout["backlog"], DEFAULT_LAYOUT.backlog),
219530
+ adr: text(layout["adr"], DEFAULT_LAYOUT.adr),
219531
+ inventory: text(layout["inventory"], DEFAULT_LAYOUT.inventory)
219532
+ },
219533
+ waves: strArray$1(base["waves"], ["W0"]),
219534
+ thresholds: {
219535
+ minEpics: num(thresholds["minEpics"], DEFAULT_THRESHOLDS.minEpics),
219536
+ minTasks: num(thresholds["minTasks"], DEFAULT_THRESHOLDS.minTasks),
219537
+ minAcPerTask: num(thresholds["minAcPerTask"], DEFAULT_THRESHOLDS.minAcPerTask),
219538
+ minDecisionBytes: num(thresholds["minDecisionBytes"], DEFAULT_THRESHOLDS.minDecisionBytes),
219539
+ minArtifactBytes: num(thresholds["minArtifactBytes"], DEFAULT_THRESHOLDS.minArtifactBytes),
219540
+ minBriefSections: num(thresholds["minBriefSections"], DEFAULT_THRESHOLDS.minBriefSections),
219541
+ minContextBytes: num(thresholds["minContextBytes"], DEFAULT_THRESHOLDS.minContextBytes),
219542
+ minGoalChars: num(thresholds["minGoalChars"], DEFAULT_THRESHOLDS.minGoalChars),
219543
+ minScopeChars: num(thresholds["minScopeChars"], DEFAULT_THRESHOLDS.minScopeChars),
219544
+ minFieldChars: num(thresholds["minFieldChars"], DEFAULT_THRESHOLDS.minFieldChars),
219545
+ minMetricChars: num(thresholds["minMetricChars"], DEFAULT_THRESHOLDS.minMetricChars)
219546
+ },
219547
+ vocabulary: {
219548
+ areas: strArray$1(vocabulary["areas"], DEFAULT_VOCABULARY.areas),
219549
+ requiredAreas: strArray$1(vocabulary["requiredAreas"], DEFAULT_VOCABULARY.requiredAreas),
219550
+ types: strArray$1(vocabulary["types"], DEFAULT_VOCABULARY.types),
219551
+ estimates: strArray$1(vocabulary["estimates"], DEFAULT_VOCABULARY.estimates),
219552
+ vagueWords: strArray$1(vocabulary["vagueWords"], DEFAULT_VOCABULARY.vagueWords),
219553
+ verifyCommands: strArray$1(vocabulary["verifyCommands"], DEFAULT_VOCABULARY.verifyCommands),
219554
+ verifyPrefixes: strArray$1(vocabulary["verifyPrefixes"], DEFAULT_VOCABULARY.verifyPrefixes)
219555
+ },
219556
+ secrets: strArray$1(base["secrets"], []),
219557
+ roles: {
219558
+ proponents: num(roles["proponents"], DEFAULT_ROLES.proponents),
219559
+ defenders: num(roles["defenders"], DEFAULT_ROLES.defenders),
219560
+ adversaries: num(roles["adversaries"], DEFAULT_ROLES.adversaries),
219561
+ judges: num(roles["judges"], DEFAULT_ROLES.judges),
219562
+ refiners: num(roles["refiners"], DEFAULT_ROLES.refiners)
219563
+ },
219564
+ quorum: {
219565
+ minPoints: num(quorum["minPoints"], DEFAULT_QUORUM.minPoints),
219566
+ minAxes: num(quorum["minAxes"], DEFAULT_QUORUM.minAxes),
219567
+ axes: strArray$1(quorum["axes"], DEFAULT_QUORUM.axes)
219568
+ },
219569
+ loadedFrom
219698
219570
  };
219699
219571
  }
219700
- var init_decideRunner = __esmMin((() => {
219701
- init_config$6();
219702
- init_nodeFs();
219703
- init_quorum();
219704
- }));
219705
- // ../../packages/agent-core-v2/src/features/wave/tools/decide/decide.md?raw
219706
- var decide_default;
219707
- var init_decide = __esmMin((() => {
219708
- decide_default = "Compute the wave quorum from the judges' totals and say what may happen next.\n\nPass one entry per judge with its totals per proposal (`{ judge: \"J1\", scores: { P1: 17, P2: 15, P3: 14 } }`). The tool takes the **median** per proposal and applies both conditions of `core/quorum.ts`: the winner's median must beat the second's by `minPoints` (**margen de medianas**), and at least `minAxes` judges must give the winner that margin or more over the second (**mayoría de jueces**). Thresholds default to `workflow.config.json`.\n\nAnswers `adopt`, `refine` (no quorum yet, compose R1 and audit with R2) or `deadlock` (two rounds without quorum: adopt R1 with written dissent). A tie in the median, or a single proposal with nothing to compare, never crowns a winner and never counts as quorum.\n";
219709
- }));
219710
- // ../../packages/agent-core-v2/src/features/wave/tools/decide/decideTool.ts
219711
- var WaveDecideTool;
219712
- var init_decideTool = __esmMin((() => {
219713
- init_sessionContext();
219714
- init_input_schema();
219715
- init_decide$1();
219716
- init_decideRunner();
219717
- init_decide();
219718
- init_decorateParam();
219719
- init_decorate();
219720
- WaveDecideTool = class WaveDecideTool {
219721
- sessionContext;
219722
- name = "WaveDecide";
219723
- description = decide_default;
219724
- parameters = toInputJsonSchema(WaveDecideToolInputSchema);
219725
- constructor(sessionContext) {
219726
- this.sessionContext = sessionContext;
219727
- }
219728
- resolveExecution(args) {
219729
- return {
219730
- description: "Computing the wave quorum",
219731
- approvalRule: this.name,
219732
- execute: () => Promise.resolve(runWaveDecideForRoot({
219733
- root: args.root ?? this.sessionContext.cwd,
219734
- verdicts: args.verdicts,
219735
- minPoints: args.minPoints,
219736
- minAxes: args.minAxes,
219737
- round: args.round
219738
- }))
219739
- };
219740
- }
219741
- };
219742
- WaveDecideTool = __decorate([__decorateParam(0, ISessionContext)], WaveDecideTool);
219743
- }));
219744
- // ../../packages/agent-core-v2/src/features/wave/tools/gate/gate.ts
219745
- var WaveGateToolInputSchema, IWaveGateTool;
219746
- var init_gate$1 = __esmMin((() => {
219747
- init_zod$1();
219748
- init_instantiation();
219749
- WaveGateToolInputSchema = object$1({
219750
- root: string().optional(),
219751
- json: boolean$1().optional()
219752
- }).strict();
219753
- IWaveGateTool = createDecorator("waveGateTool");
219754
- }));
219755
- // ../../packages/agent-core-v2/src/features/wave/tools/open/open.ts
219756
- var WaveOpenToolInputSchema, IWaveOpenTool;
219757
- var init_open$1 = __esmMin((() => {
219758
- init_zod$1();
219759
- init_instantiation();
219760
- WaveOpenToolInputSchema = object$1({
219761
- name: string().min(2),
219762
- id: string().optional(),
219763
- root: string().optional()
219764
- }).strict();
219765
- IWaveOpenTool = createDecorator("waveOpenTool");
219766
- }));
219767
- // ../../packages/agent-core-v2/src/features/wave/core/fsJson.ts
219768
- function readJson$2(fs, path) {
219769
- const text = fs.read(path);
219770
- if (text === void 0) return void 0;
219572
+ function readWaveConfig(fs, root) {
219573
+ const file = resolve(root, "workflow.config.json");
219574
+ const raw = fs.read(file);
219575
+ if (raw === void 0) return void 0;
219771
219576
  try {
219772
- return JSON.parse(text);
219577
+ return makeWaveConfig(JSON.parse(raw), file);
219773
219578
  } catch {
219774
219579
  return;
219775
219580
  }
219776
219581
  }
219777
- function isRecord$16(value) {
219778
- return typeof value === "object" && value !== null && !Array.isArray(value);
219779
- }
219780
- function asRecord$2(value) {
219781
- return isRecord$16(value) ? value : {};
219782
- }
219783
- function asArray$2(value) {
219784
- return Array.isArray(value) ? value : [];
219785
- }
219786
- function asText$1(value) {
219787
- return typeof value === "string" ? value : "";
219788
- }
219789
- function writeJson(_fs, path, value) {
219790
- mkdirSync(dirname(path), { recursive: true });
219791
- writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
219792
- }
219793
- function walk$1(fs, dir, accept, acc = []) {
219794
- if (!fs.isDirectory(dir)) return acc;
219795
- for (const name of fs.list(dir)) {
219796
- const full = join(dir, name);
219797
- if (fs.isDirectory(full)) walk$1(fs, full, accept, acc);
219798
- else if (accept(full)) acc.push(full);
219799
- }
219800
- return acc;
219801
- }
219802
- var init_fsJson = __esmMin((() => {}));
219803
- // ../../packages/agent-core-v2/src/features/wave/tools/open/openRunner.ts
219804
- function briefTemplate(root) {
219805
- const fs = nodeWaveFs;
219806
- const local = join(root, BRIEF_TEMPLATE$1);
219807
- if (fs.exists(local)) return { file: local };
219808
- if (fs.isDirectory(join(root, "workflow"))) return { missing: BRIEF_TEMPLATE$1 };
219809
- return fs.exists(BUNDLED_BRIEF_TEMPLATE) ? { file: BUNDLED_BRIEF_TEMPLATE } : { missing: BRIEF_TEMPLATE$1 };
219810
- }
219811
- function nextEpic(backlogDir) {
219812
- const numbers = asArray$2(asRecord$2(readJson$2(nodeWaveFs, join(backlogDir, "epics.json")) ?? {})["epics"]).map((epic) => /^E(\d{2,})$/.exec(String(asRecord$2(epic)["id"]))?.[1]).filter((value) => value !== void 0).map(Number);
219813
- const next = numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
219814
- return `E${String(next).padStart(2, "0")}`;
219815
- }
219816
- function slugify(name) {
219817
- return name.normalize("NFD").replaceAll(/[\u0300-\u036F]/g, "").toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "");
219582
+ function containedPath(root, candidate) {
219583
+ const resolvedRoot = resolve(root);
219584
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(resolvedRoot, candidate);
219585
+ if (resolved !== resolvedRoot && !resolved.startsWith(`${resolvedRoot}${sep}`)) throw new Error(`ruta fuera del workspace: ${candidate}`);
219586
+ return resolved;
219818
219587
  }
219819
- function runWaveOpenForRoot(input) {
219820
- const fs = nodeWaveFs;
219821
- const configFile = join(input.root, "workflow.config.json");
219822
- const rawConfig = readJson$2(fs, configFile);
219823
- if (rawConfig === void 0) return {
219824
- output: `no hay workflow.config.json en ${input.root}`,
219825
- isError: true
219826
- };
219827
- const layout = asRecord$2(asRecord$2(rawConfig)["layout"]);
219828
- const wavesRel = typeof layout["waves"] === "string" ? layout["waves"] : "design/waves";
219829
- const backlogRel = typeof layout["backlog"] === "string" ? layout["backlog"] : "backlog";
219830
- const minimum = makeWaveConfig(rawConfig).thresholds.minBriefSections;
219831
- let wavesDir;
219832
- let backlogDir;
219833
- try {
219834
- wavesDir = containedPath(input.root, wavesRel);
219835
- backlogDir = containedPath(input.root, backlogRel);
219836
- } catch (error) {
219837
- return {
219838
- output: error instanceof Error ? error.message : String(error),
219839
- isError: true
219840
- };
219841
- }
219842
- const template = briefTemplate(input.root);
219843
- if (template.file === void 0) return {
219844
- output: `falta ${template.missing ?? BRIEF_TEMPLATE$1}: no se genera un brief sin la plantilla unica`,
219845
- isError: true
219588
+ var WAVE_ROUNDS, WAVE_ROUND_DIRS, DEFAULT_LAYOUT, DEFAULT_THRESHOLDS, DEFAULT_VOCABULARY, DEFAULT_ROLES, DEFAULT_QUORUM;
219589
+ var init_config$6 = __esmMin((() => {
219590
+ WAVE_ROUNDS = [
219591
+ "propose",
219592
+ "defend",
219593
+ "attack",
219594
+ "judge",
219595
+ "refine",
219596
+ "audit"
219597
+ ];
219598
+ WAVE_ROUND_DIRS = {
219599
+ propose: "01-proposals",
219600
+ defend: "02-defenses",
219601
+ attack: "03-attacks",
219602
+ judge: "04-verdicts",
219603
+ refine: "05-refined",
219604
+ audit: "05-refined"
219846
219605
  };
219847
- const source = fs.read(template.file);
219848
- if (source === void 0) return {
219849
- output: `no se pudo leer ${template.file}`,
219850
- isError: true
219606
+ DEFAULT_LAYOUT = {
219607
+ waves: "design/waves",
219608
+ backlog: "backlog",
219609
+ adr: "design/ADR",
219610
+ inventory: "inventory"
219851
219611
  };
219852
- const existing = fs.list(wavesDir).filter((name) => fs.isDirectory(join(wavesDir, name))).map((name) => Number(/^W(\d+)/.exec(name)?.[1] ?? NaN)).filter((value) => !Number.isNaN(value));
219853
- const next = input.id ?? `W${existing.length > 0 ? Math.max(...existing) + 1 : 0}`;
219854
- if (!/^W\d{1,2}$/.test(next)) return {
219855
- output: `id de oleada invalido "${next}"`,
219856
- isError: true
219612
+ DEFAULT_THRESHOLDS = {
219613
+ minEpics: 1,
219614
+ minTasks: 1,
219615
+ minAcPerTask: 2,
219616
+ minDecisionBytes: 200,
219617
+ minArtifactBytes: 100,
219618
+ minBriefSections: 7,
219619
+ minContextBytes: 80,
219620
+ minGoalChars: 40,
219621
+ minScopeChars: 40,
219622
+ minFieldChars: 10,
219623
+ minMetricChars: 4
219857
219624
  };
219858
- const slug = slugify(input.name);
219859
- if (slug === "") return {
219860
- output: "el nombre de la oleada queda vacio al normalizarlo",
219861
- isError: true
219625
+ DEFAULT_VOCABULARY = {
219626
+ areas: [
219627
+ "cliente",
219628
+ "seller",
219629
+ "agent",
219630
+ "admin",
219631
+ "plataforma"
219632
+ ],
219633
+ requiredAreas: [],
219634
+ types: [
219635
+ "db",
219636
+ "api",
219637
+ "ui",
219638
+ "domain",
219639
+ "infra",
219640
+ "test",
219641
+ "docs",
219642
+ "app",
219643
+ "contract",
219644
+ "ops",
219645
+ "tooling"
219646
+ ],
219647
+ estimates: [
219648
+ "XS",
219649
+ "S",
219650
+ "M",
219651
+ "L",
219652
+ "XL"
219653
+ ],
219654
+ vagueWords: [
219655
+ "funciona",
219656
+ "correcto",
219657
+ "adecuado",
219658
+ "rapido",
219659
+ "robusto",
219660
+ "bonito"
219661
+ ],
219662
+ verifyCommands: [
219663
+ "pnpm",
219664
+ "npm",
219665
+ "node",
219666
+ "bash",
219667
+ "sh",
219668
+ "make",
219669
+ "python",
219670
+ "go",
219671
+ "cargo",
219672
+ "dotnet"
219673
+ ],
219674
+ verifyPrefixes: [
219675
+ "test/unit/",
219676
+ "test/db/",
219677
+ "test/domain/",
219678
+ "test/api/",
219679
+ "test/perf/",
219680
+ "e2e/",
219681
+ "scripts/checks/"
219682
+ ]
219862
219683
  };
219863
- if (fs.list(wavesDir).some((name) => name === next || name.startsWith(`${next}-`))) return {
219864
- output: `la oleada ${next} ya existe`,
219865
- isError: true
219684
+ DEFAULT_ROLES = {
219685
+ proponents: 3,
219686
+ defenders: 3,
219687
+ adversaries: 3,
219688
+ judges: 3,
219689
+ refiners: 2
219866
219690
  };
219867
- const dir = join(wavesDir, `${next}-${slug}`);
219868
- const brief = source.replaceAll("{{WAVE}}", next).replaceAll("{{NAME}}", slug).replaceAll("{{NEXT_EPIC}}", nextEpic(backlogDir));
219869
- for (const sub of SUBDIRS) fs.mkdir(join(dir, sub));
219870
- fs.write(join(dir, "00-brief.md"), brief);
219871
- const declared = asArray$2(asRecord$2(rawConfig)["waves"]).map(String);
219872
- if (!declared.includes(next)) writeJson(fs, configFile, {
219873
- ...asRecord$2(rawConfig),
219874
- waves: [...declared, next].toSorted()
219875
- });
219876
- return { output: [
219877
- `oleada creada: ${next}-${slug}`,
219878
- ` brief: ${wavesRel}/${next}-${slug}/00-brief.md (rellena las ${minimum} secciones antes de lanzar asientos)`,
219879
- ` config: ${next} declarada en workflow.config.json (el gate la exige desde ahora)`
219880
- ].join("\n") };
219881
- }
219882
- var SUBDIRS, BRIEF_TEMPLATE$1, BUNDLED_BRIEF_TEMPLATE;
219883
- var init_openRunner = __esmMin((() => {
219884
- init_config$6();
219885
- init_fsJson();
219886
- init_nodeFs();
219887
- SUBDIRS = [
219888
- "01-proposals",
219889
- "02-defenses",
219890
- "03-attacks",
219891
- "04-verdicts",
219892
- "05-refined"
219893
- ];
219894
- BRIEF_TEMPLATE$1 = "workflow/templates/brief.md";
219895
- BUNDLED_BRIEF_TEMPLATE = resolve(import.meta.dirname, "../../../../../../../workflow/templates/brief.md");
219896
- }));
219897
- // ../../packages/agent-core-v2/src/features/wave/tools/open/open.md?raw
219898
- var open_default;
219899
- var init_open = __esmMin((() => {
219900
- open_default = "Open a wave: create its artifact tree and a brief skeleton, and declare the wave in `workflow.config.json`.\n\nThe brief starts with the seven mandatory sections and nothing else — a brief that already contains the answer makes the twelve seats theatre. Fill it before launching any seat; the gate rejects a brief with fewer than seven sections.\n\nOpening a wave also declares it in the config, so the package gate will require its artifacts from then on.\n";
219901
- }));
219902
- // ../../packages/agent-core-v2/src/features/wave/tools/open/openTool.ts
219903
- var WaveOpenTool;
219904
- var init_openTool = __esmMin((() => {
219905
- init_sessionContext();
219906
- init_input_schema();
219907
- init_open$1();
219908
- init_openRunner();
219909
- init_open();
219910
- init_decorateParam();
219911
- init_decorate();
219912
- WaveOpenTool = class WaveOpenTool {
219913
- sessionContext;
219914
- name = "WaveOpen";
219915
- description = open_default;
219916
- parameters = toInputJsonSchema(WaveOpenToolInputSchema);
219917
- constructor(sessionContext) {
219918
- this.sessionContext = sessionContext;
219919
- }
219920
- resolveExecution(args) {
219921
- return {
219922
- description: `Opening wave ${args.name}`,
219923
- approvalRule: this.name,
219924
- execute: () => Promise.resolve(runWaveOpenForRoot({
219925
- root: args.root ?? this.sessionContext.cwd,
219926
- name: args.name,
219927
- id: args.id
219928
- }))
219929
- };
219930
- }
219691
+ DEFAULT_QUORUM = {
219692
+ minPoints: 1,
219693
+ minAxes: 2,
219694
+ axes: [
219695
+ "cobertura",
219696
+ "falsabilidad",
219697
+ "riesgo",
219698
+ "coste",
219699
+ "encaje"
219700
+ ]
219931
219701
  };
219932
- WaveOpenTool = __decorate([__decorateParam(0, ISessionContext)], WaveOpenTool);
219933
- }));
219934
- // ../../packages/agent-core-v2/src/features/wave/tools/round/round.ts
219935
- var WaveRoundToolInputSchema, IWaveRoundTool;
219936
- var init_round$1 = __esmMin((() => {
219937
- init_zod$1();
219938
- init_instantiation();
219939
- WaveRoundToolInputSchema = object$1({
219940
- wave: string().min(2),
219941
- round: _enum([
219942
- "propose",
219943
- "defend",
219944
- "attack",
219945
- "judge",
219946
- "refine",
219947
- "audit"
219948
- ]),
219949
- root: string().optional()
219950
- }).strict();
219951
- IWaveRoundTool = createDecorator("waveRoundTool");
219952
219702
  }));
219953
- // ../../packages/agent-core-v2/src/features/wave/tools/submit/submitRunner.ts
219703
+ // ../../packages/agent-core-v2/src/features/wave/core/seats.ts
219704
+ function contractedSeats(fs, registryFile) {
219705
+ const raw = fs.read(registryFile);
219706
+ if (raw === void 0) return void 0;
219707
+ try {
219708
+ return (JSON.parse(raw).seats ?? []).filter((seat) => typeof seat["round"] === "string" && typeof seat["role"] === "string").map((seat) => ({
219709
+ round: String(seat["round"]),
219710
+ role: String(seat["role"])
219711
+ }));
219712
+ } catch {
219713
+ return;
219714
+ }
219715
+ }
219954
219716
  function seatTicketHash(ticket) {
219955
219717
  return createHash("sha256").update(ticket).digest("hex");
219956
219718
  }
219957
- function findWaveDir(root, wavesRel, wave) {
219719
+ function waveBase(input) {
219958
219720
  const fs = nodeWaveFs;
219959
- const wavesDir = containedPath(root, wavesRel);
219960
- const name = fs.list(wavesDir).filter((entry) => fs.isDirectory(join(wavesDir, entry))).find((entry) => entry === wave || entry.startsWith(`${wave}-`));
219961
- return name === void 0 ? void 0 : join(wavesDir, name);
219721
+ const rawConfig = readJson$2(fs, join(input.root, "workflow.config.json"));
219722
+ if (rawConfig === void 0) return { error: `no hay workflow.config.json en ${input.root}` };
219723
+ const wavesRel = makeWaveConfig(rawConfig).layout.waves;
219724
+ let wavesDir;
219725
+ try {
219726
+ wavesDir = containedPath(input.root, wavesRel);
219727
+ } catch (error) {
219728
+ return { error: error instanceof Error ? error.message : String(error) };
219729
+ }
219730
+ const name = fs.list(wavesDir).filter((entry) => fs.isDirectory(join(wavesDir, entry))).find((entry) => entry === input.wave || entry.startsWith(`${input.wave}-`));
219731
+ if (name === void 0) return { error: `no existe la oleada ${input.wave} en ${wavesRel}` };
219732
+ return {
219733
+ base: join(wavesDir, name),
219734
+ wavesRel
219735
+ };
219962
219736
  }
219963
- function runWaveSubmitForRoot(input) {
219737
+ function runWaveContract(input) {
219964
219738
  const fs = nodeWaveFs;
219965
219739
  const rawConfig = readJson$2(fs, join(input.root, "workflow.config.json"));
219966
219740
  if (rawConfig === void 0) return {
219967
219741
  output: `no hay workflow.config.json en ${input.root}`,
219742
+ seats: [],
219968
219743
  isError: true
219969
219744
  };
219970
- const config = makeWaveConfig(rawConfig);
219971
- const wavesRel = config.layout.waves;
219972
- const seats = waveRoundSeats(config.roles, input.round);
219973
- if (!seats.includes(input.role)) return {
219974
- output: `seats.unknown_seat: el rol "${input.role}" no pertenece a la ronda ${input.round} (esperado ${seats.join(", ")})`,
219745
+ const located = waveBase(input);
219746
+ if (located.base === void 0) return {
219747
+ output: located.error ?? "oleada no encontrada",
219748
+ seats: [],
219975
219749
  isError: true
219976
219750
  };
219977
- let base;
219978
- try {
219979
- base = findWaveDir(input.root, wavesRel, input.wave);
219980
- } catch (error) {
219751
+ const base = located.base;
219752
+ const roundDir = WAVE_ROUND_DIRS[input.round];
219753
+ const roles = waveRoundSeats(makeWaveConfig(rawConfig).roles, input.round);
219754
+ if (input.report === true) {
219755
+ const delivered = roles.filter((role) => fs.exists(join(base, roundDir, `${role}.md`)));
219756
+ const missing = roles.filter((role) => !delivered.includes(role));
219981
219757
  return {
219982
- output: error instanceof Error ? error.message : String(error),
219983
- isError: true
219758
+ output: [
219759
+ `CONTRATOS ${input.round} de ${input.wave}: ${delivered.length}/${roles.length} entregados`,
219760
+ ...roles.map((role) => `${delivered.includes(role) ? "ok " : "FIJA"} ${role} ${roundDir}/${role}.md`),
219761
+ ...missing.length === 0 ? [] : ["", `faltan: ${missing.join(", ")}`]
219762
+ ].join("\n"),
219763
+ seats: [],
219764
+ isError: missing.length > 0
219984
219765
  };
219985
219766
  }
219986
- if (base === void 0) return {
219987
- output: `no existe la oleada ${input.wave} en ${wavesRel}`,
219988
- isError: true
219989
- };
219990
- const dirName = WAVE_ROUND_DIRS[input.round];
219991
- const dir = join(base, dirName);
219992
- if (!fs.isDirectory(dir)) return {
219993
- output: `falta el directorio ${dirName} en la oleada ${input.wave}`,
219994
- isError: true
219995
- };
219996
- const seatsDir = join(base, WAVE_SEATS_DIR);
219997
- if (fs.isDirectory(seatsDir) && fs.list(seatsDir).length > 0) {
219998
- const presented = (input.ticket ?? "").trim();
219999
- if (presented === "") return {
220000
- output: `seats.foreign_replace: el asiento ${input.role} no presento el ticket que emitio la ronda`,
219767
+ const declared = input.seats ?? [];
219768
+ for (const seat of declared) {
219769
+ if (seat.role !== void 0 && !roles.includes(seat.role)) return {
219770
+ output: `rol desconocido "${seat.role}" para la ronda ${input.round}: ${roles.join(", ")}`,
219771
+ seats: [],
220001
219772
  isError: true
220002
219773
  };
220003
- const stored = (fs.read(join(seatsDir, input.role)) ?? "").trim();
220004
- if (stored === "" || stored !== seatTicketHash(presented)) return {
220005
- output: `seats.foreign_replace: el ticket presentado no es el del asiento ${input.role}`,
219774
+ if (seat.artifact !== void 0 && !seat.artifact.startsWith(`${roundDir}/`)) return {
219775
+ output: `el artefacto "${seat.artifact}" queda fuera de ${roundDir}/`,
219776
+ seats: [],
220006
219777
  isError: true
220007
219778
  };
220008
- if (input.replace === true && (input.reason ?? "").trim() === "") return {
220009
- output: `seats.replace_without_reason: reescribir el artefacto del asiento ${input.role} exige un motivo no vacio`,
219779
+ }
219780
+ const requested = declared.length === 0 ? roles.map((role) => ({ role })) : declared;
219781
+ const resolved = [];
219782
+ for (const [index, seat] of requested.entries()) {
219783
+ const role = seat.role ?? roles[index];
219784
+ if (role === void 0) return {
219785
+ output: "se contrataron más asientos que roles tiene la ronda",
219786
+ seats: [],
220010
219787
  isError: true
220011
219788
  };
219789
+ resolved.push({
219790
+ role,
219791
+ file: seat.artifact ?? `${roundDir}/${role}.md`
219792
+ });
220012
219793
  }
220013
- const file = join(dir, `${input.role}.md`);
220014
- if (fs.exists(file) && input.replace !== true) return {
220015
- output: `el asiento ${input.role} ya entrego su artefacto; un asiento escribe una vez (usa replace solo para relanzar un fallo)`,
220016
- isError: true
220017
- };
220018
- if (input.artifact.trim() === "") return {
220019
- output: "el artefacto esta vacio: un asiento que no escribe, fallo",
220020
- isError: true
219794
+ const seatsDir = join(base, WAVE_SEATS_DIR);
219795
+ const entries = asArray$2(asRecord$2(readJson$2(fs, join(seatsDir, "seats.json")) ?? {})["seats"]).map(asRecord$2).filter((entry) => !resolved.some((seat) => entry["round"] === input.round && entry["role"] === seat.role));
219796
+ const contracts = [];
219797
+ for (const seat of resolved) {
219798
+ const ticket = randomBytes(16).toString("hex");
219799
+ fs.write(join(seatsDir, seat.role), `${seatTicketHash(ticket)}\n`);
219800
+ entries.push({
219801
+ role: seat.role,
219802
+ round: input.round,
219803
+ file: seat.file,
219804
+ ticket: seatTicketHash(ticket)
219805
+ });
219806
+ contracts.push({
219807
+ ...seat,
219808
+ ticket,
219809
+ prompt: input.buildPrompt({
219810
+ wave: input.wave,
219811
+ round: input.round,
219812
+ ...seat,
219813
+ ticket
219814
+ })
219815
+ });
219816
+ }
219817
+ writeJson(fs, join(seatsDir, "seats.json"), { seats: entries });
219818
+ return {
219819
+ output: [`CONTRATOS ${input.round} de ${input.wave}: ${contracts.length} asiento(s)`, ...contracts.map((contract) => `\n=== ${contract.role} -> ${contract.file}\n${contract.prompt}`)].join("\n"),
219820
+ seats: contracts
220021
219821
  };
220022
- fs.write(file, input.artifact.endsWith("\n") ? input.artifact : `${input.artifact}\n`);
220023
- return { output: `escrito ${wavesRel}/${base.split("/").pop()}/${dirName}/${input.role}.md` };
220024
219822
  }
220025
- var WAVE_SEATS_DIR;
220026
- var init_submitRunner = __esmMin((() => {
219823
+ var WAVE_SEATS_DIR, WAVE_CONTRACTS_FILE;
219824
+ var init_seats = __esmMin((() => {
220027
219825
  init_config$6();
220028
219826
  init_fsJson();
220029
219827
  init_nodeFs();
220030
219828
  WAVE_SEATS_DIR = ".seats";
219829
+ WAVE_CONTRACTS_FILE = `${WAVE_SEATS_DIR}/seats.json`;
220031
219830
  }));
220032
219831
  // ../../packages/agent-core-v2/src/features/wave/tools/round/prompts.ts
220033
219832
  var SEAT_PRELUDE, ROLE_PROMPTS, JUDGE_BIASES;
@@ -220055,188 +219854,290 @@ var init_prompts$2 = __esmMin((() => {
220055
219854
  "encaje"
220056
219855
  ];
220057
219856
  }));
220058
- // ../../packages/agent-core-v2/src/features/wave/tools/round/roundRunner.ts
220059
- function runWaveRoundForRoot(input) {
220060
- const fs = nodeWaveFs;
220061
- const rawConfig = readJson$2(fs, join(input.root, "workflow.config.json"));
220062
- if (rawConfig === void 0) return {
220063
- output: `no hay workflow.config.json en ${input.root}`,
220064
- seats: [],
220065
- isError: true
220066
- };
220067
- const config = makeWaveConfig(rawConfig);
220068
- const wavesRel = config.layout.waves;
220069
- let wavesDir;
220070
- try {
220071
- wavesDir = containedPath(input.root, wavesRel);
220072
- } catch (error) {
220073
- return {
220074
- output: error instanceof Error ? error.message : String(error),
220075
- seats: [],
220076
- isError: true
220077
- };
220078
- }
220079
- const waveDir = fs.list(wavesDir).filter((name) => fs.isDirectory(join(wavesDir, name))).find((name) => name === input.wave || name.startsWith(`${input.wave}-`));
220080
- if (waveDir === void 0) return {
220081
- output: `no existe la oleada ${input.wave} en ${wavesRel}`,
220082
- seats: [],
220083
- isError: true
220084
- };
220085
- const briefRel = `${wavesRel}/${waveDir}/00-brief.md`;
220086
- const brief = fs.read(join(wavesDir, waveDir, "00-brief.md"));
220087
- if (brief === void 0) return {
220088
- output: `falta ${briefRel}`,
220089
- seats: [],
220090
- isError: true
220091
- };
220092
- const sections = brief.match(/^##\s+\S/gm)?.length ?? 0;
220093
- const minimum = config.thresholds.minBriefSections;
220094
- if (sections < minimum) return {
220095
- output: `el brief tiene ${sections} secciones < ${minimum}: rellena las ${minimum} antes de lanzar la ronda`,
220096
- seats: [],
220097
- isError: true
220098
- };
220099
- const dir = WAVE_ROUND_DIRS[input.round];
220100
- const seatsDir = join(wavesDir, waveDir, WAVE_SEATS_DIR);
220101
- const seats = waveRoundSeats(config.roles, input.round).map((role) => {
220102
- const bias = input.round === "judge" ? JUDGE_BIASES[(Number(role.slice(1)) - 1) % JUDGE_BIASES.length] : void 0;
220103
- const file = `${wavesRel}/${waveDir}/${dir}/${role}.md`;
220104
- const ticket = randomBytes(16).toString("hex");
220105
- fs.write(join(seatsDir, role), `${seatTicketHash(ticket)}\n`);
220106
- const lines = [
220107
- SEAT_PRELUDE,
220108
- "",
220109
- ROLE_PROMPTS[input.round] ?? "",
220110
- "",
220111
- `Perfil: ${WAVE_SEAT_PROFILE}`,
220112
- `Oleada: ${input.wave}. Brief: ${briefRel}.`,
220113
- `Escribe: ${file}.`,
220114
- `Ticket de asiento: ${ticket}. Es capacidad, no identidad: la ronda lo emitio para este asiento y WaveSubmit lo exige.`,
220115
- `Entrega con WaveSubmit({ wave: "${input.wave}", round: "${input.round}", role: "${role}", ticket: "${ticket}", artifact: "..." }).`
220116
- ];
220117
- if (bias !== void 0) lines.push(`Tu sesgo declarado es ${bias}: puntua CON el, no contra el.`);
220118
- return {
220119
- role,
220120
- file,
220121
- ticket,
220122
- prompt: lines.join("\n")
220123
- };
219857
+ // ../../packages/agent-core-v2/src/features/wave/tools/contract/contractRunner.ts
219858
+ function briefRel(root, wave) {
219859
+ const wavesRel = readJson$2(nodeWaveFs, join(root, "workflow.config.json"))?.layout?.waves ?? "design/waves";
219860
+ return `${wavesRel}/${nodeWaveFs.list(join(root, wavesRel)).find((entry) => entry === wave || entry.startsWith(`${wave}-`)) ?? wave}/00-brief.md`;
219861
+ }
219862
+ function runWaveContractForRoot(input) {
219863
+ const brief = briefRel(input.root, input.wave);
219864
+ return runWaveContract({
219865
+ ...input,
219866
+ buildPrompt: (seat) => {
219867
+ const bias = input.round === "judge" ? JUDGE_BIASES[(Number(seat.role.slice(1)) - 1) % JUDGE_BIASES.length] : void 0;
219868
+ const lines = [
219869
+ SEAT_PRELUDE,
219870
+ "",
219871
+ ROLE_PROMPTS[input.round] ?? "",
219872
+ "",
219873
+ `Perfil: ${WAVE_SEAT_PROFILE}`,
219874
+ `Oleada: ${input.wave}. Brief: ${brief}.`,
219875
+ `Escribe: ${seat.file}.`,
219876
+ `Ticket de asiento: ${seat.ticket}. Es capacidad, no identidad: el contrato lo emitió para este asiento y WaveSubmit lo exige.`,
219877
+ `Entrega con WaveSubmit({ wave: "${input.wave}", round: "${input.round}", role: "${seat.role}", ticket: "${seat.ticket}", artifact: "..." }).`
219878
+ ];
219879
+ if (bias !== void 0) lines.push(`Tu sesgo declarado es ${bias}: puntúa CON él, no contra él.`);
219880
+ return lines.join("\n");
219881
+ }
220124
219882
  });
220125
- return {
220126
- output: [`RONDA ${input.round} de ${input.wave}: ${seats.length} asiento(s)`, ...seats.map((seat) => `\n=== ${seat.role} -> ${seat.file}\n${seat.prompt}`)].join("\n"),
220127
- seats
220128
- };
220129
219883
  }
220130
- var init_roundRunner = __esmMin((() => {
220131
- init_config$6();
219884
+ var init_contractRunner = __esmMin((() => {
220132
219885
  init_fsJson();
220133
219886
  init_nodeFs();
220134
- init_wave$3();
220135
- init_submitRunner();
219887
+ init_seats();
220136
219888
  init_prompts$2();
219889
+ init_wave$3();
220137
219890
  }));
220138
- // ../../packages/agent-core-v2/src/features/wave/tools/round/round.md?raw
220139
- var round_default;
220140
- var init_round = __esmMin((() => {
220141
- round_default = "Plan one round of a wave: it returns the exact prompt for every seat, ready to launch.\n\nRounds and seats come from `roles.*` in `workflow.config.json`: `propose` gives P1..Pn, `defend` D1..Dn, `attack` A1..An, `judge` J1..Jn (each with a declared bias: coste, falsabilidad, encaje), `refine` R1, `audit` R2.\n\nThis tool does not spawn anything. The orchestrator launches one agent per seat with the returned prompt — `Agent` for a single seat, `AgentSwarm` when the user has enabled swarm mode — and each seat delivers its artifact with WaveSubmit. That keeps fan-out under the user's consent instead of hiding it behind a tool.\n\nThe brief must exist with its seven sections: a round built on a thin brief produces twelve incompatible problems.\n";
219891
+ // ../../packages/agent-core-v2/src/features/wave/tools/contract/contract.md?raw
219892
+ var contract_default;
219893
+ var init_contract$3 = __esmMin((() => {
219894
+ contract_default = "Create the round's contracts and hand each seat its exact prompt.\n\nReturns one contract per seat: `role`, `artifact` path, `ticket` and the ready-to-launch prompt. Launch one agent per contract with `Agent(subagent_type: \"wave-seat\")` (or `AgentSwarm` when the user enabled swarm) and pass the prompt unchanged — the ticket in it is what `WaveSubmit` accepts.\n\n- Seats and artifact paths come from `roles.*` in `workflow.config.json` and the round's directory. You may narrow them with `seats` (`role`, `artifact`), but an artifact must stay inside the round's directory.\n- You do not write artifacts: `WaveSubmit` is the seat's tool. Contract, launch, then read the results.\n- `report: true` lists which seats of the round have already delivered, without issuing tickets.\n\nClose the round with `WaveDecide` (judges), `WaveVerify` (tasks) and `WaveGate` (package). The wave is not closed until the gate is green.\n";
220142
219895
  }));
220143
- // ../../packages/agent-core-v2/src/features/wave/tools/round/roundTool.ts
220144
- var WaveRoundTool;
220145
- var init_roundTool = __esmMin((() => {
219896
+ // ../../packages/agent-core-v2/src/features/wave/tools/contract/contractTool.ts
219897
+ var WaveContractTool;
219898
+ var init_contractTool = __esmMin((() => {
220146
219899
  init_sessionContext();
220147
219900
  init_input_schema();
220148
- init_round$1();
220149
- init_roundRunner();
220150
- init_round();
219901
+ init_contract$4();
219902
+ init_contractRunner();
219903
+ init_contract$3();
220151
219904
  init_decorateParam();
220152
219905
  init_decorate();
220153
- WaveRoundTool = class WaveRoundTool {
219906
+ WaveContractTool = class WaveContractTool {
220154
219907
  sessionContext;
220155
- name = "WaveRound";
220156
- description = round_default;
220157
- parameters = toInputJsonSchema(WaveRoundToolInputSchema);
219908
+ name = "WaveContract";
219909
+ description = contract_default;
219910
+ parameters = toInputJsonSchema(WaveContractToolInputSchema);
220158
219911
  constructor(sessionContext) {
220159
219912
  this.sessionContext = sessionContext;
220160
219913
  }
220161
219914
  resolveExecution(args) {
220162
219915
  return {
220163
- description: `Planning ${args.round} for ${args.wave}`,
219916
+ description: `Contracting ${args.round} seats of ${args.wave}`,
220164
219917
  approvalRule: this.name,
220165
- execute: () => Promise.resolve(runWaveRoundForRoot({
219918
+ execute: () => Promise.resolve(runWaveContractForRoot({
220166
219919
  root: args.root ?? this.sessionContext.cwd,
220167
219920
  wave: args.wave,
220168
- round: args.round
219921
+ round: args.round,
219922
+ seats: args.seats,
219923
+ report: args.report
220169
219924
  }))
220170
219925
  };
220171
219926
  }
220172
219927
  };
220173
- WaveRoundTool = __decorate([__decorateParam(0, ISessionContext)], WaveRoundTool);
219928
+ WaveContractTool = __decorate([__decorateParam(0, ISessionContext)], WaveContractTool);
220174
219929
  }));
220175
- // ../../packages/agent-core-v2/src/features/wave/tools/submit/submit.ts
220176
- var WaveSubmitToolInputSchema, IWaveSubmitTool;
220177
- var init_submit$1 = __esmMin((() => {
219930
+ // ../../packages/agent-core-v2/src/features/wave/tools/decide/decide.ts
219931
+ var WaveDecideToolInputSchema, IWaveDecideTool;
219932
+ var init_decide$1 = __esmMin((() => {
220178
219933
  init_zod$1();
220179
219934
  init_instantiation();
220180
- WaveSubmitToolInputSchema = object$1({
220181
- wave: string().min(2),
220182
- round: _enum([
220183
- "propose",
220184
- "defend",
220185
- "attack",
220186
- "judge",
220187
- "refine",
220188
- "audit"
220189
- ]),
220190
- role: string().min(2),
220191
- artifact: string().min(1),
220192
- ticket: string().optional(),
220193
- reason: string().optional(),
220194
- replace: boolean$1().optional(),
220195
- root: string().optional()
219935
+ WaveDecideToolInputSchema = object$1({
219936
+ root: string().optional(),
219937
+ verdicts: array(object$1({
219938
+ judge: string().min(1),
219939
+ scores: record$3(string(), number$1())
219940
+ }).strict()).min(1),
219941
+ minPoints: number$1().optional(),
219942
+ minAxes: number$1().optional(),
219943
+ round: number$1().optional()
220196
219944
  }).strict();
220197
- IWaveSubmitTool = createDecorator("waveSubmitTool");
219945
+ IWaveDecideTool = createDecorator("waveDecideTool");
220198
219946
  }));
220199
- // ../../packages/agent-core-v2/src/features/wave/tools/submit/submit.md?raw
220200
- var submit_default;
220201
- var init_submit = __esmMin((() => {
220202
- submit_default = "Deliver the artifact for your seat. This is the only way a seat writes: the tool binds the role to its slot and refuses anything else.\n\nRole and round must match — `propose` accepts P1..Pn, `defend` D1..Dn, `attack` A1..An, `judge` J1..Jn, `refine` R1, `audit` R2..Rn. A seat writes once; use `replace` only to relaunch a failed seat after its artifact was already written.\n\nPass the `ticket` the round gave you: the tool hashes it and compares it with the sha256 the round left in `design/waves/<W>/.seats/<role>`. A missing ticket, or the ticket of another seat, is refused as `seats.foreign_replace`. The ticket is capacity, not identity (capacidad, no identidad): it proves the orchestrator issued this seat, it does not say who wrote the artifact. `replace` also needs a non-empty `reason`, or the tool refuses it as `seats.replace_without_reason`: a silent overwrite is the substitution this tool exists to prevent.\n\nPass the artifact as the text/JSON body. An empty artifact is rejected: a seat that did not write has failed, and the round must be relaunched rather than summarized by the orchestrator.\n";
219947
+ // ../../packages/agent-core-v2/src/features/wave/core/quorum.ts
219948
+ function median(values) {
219949
+ const sorted = [...values].toSorted((left, right) => left - right);
219950
+ const middle = Math.floor(sorted.length / 2);
219951
+ const current = sorted[middle];
219952
+ if (current === void 0) throw new Error("no se puede calcular una mediana sin valores");
219953
+ if (sorted.length % 2 === 1) return current;
219954
+ const previous = sorted[middle - 1];
219955
+ if (previous === void 0) throw new Error("no se puede calcular una mediana sin valores");
219956
+ return (previous + current) / 2;
219957
+ }
219958
+ function validate$1(verdicts) {
219959
+ if (verdicts.length === 0) throw new Error("el quorum exige al menos un veredicto");
219960
+ const first = verdicts[0];
219961
+ if (first === void 0) throw new Error("el quorum exige al menos un veredicto");
219962
+ const proposals = Object.keys(first.scores).toSorted();
219963
+ if (proposals.length === 0) throw new Error("el veredicto no puntua ninguna propuesta");
219964
+ for (const verdict of verdicts) {
219965
+ const keys = Object.keys(verdict.scores).toSorted();
219966
+ if (keys.length !== proposals.length || keys.some((key, index) => key !== proposals[index])) throw new Error(`el juez ${verdict.judge} no puntua el mismo conjunto de propuestas`);
219967
+ for (const proposal of proposals) {
219968
+ const score = verdict.scores[proposal];
219969
+ if (typeof score !== "number" || !Number.isFinite(score) || score < 0) throw new Error(`puntuacion invalida de ${verdict.judge} para ${proposal}`);
219970
+ }
219971
+ }
219972
+ return proposals;
219973
+ }
219974
+ function evaluateQuorum(input) {
219975
+ const proposals = validate$1(input.verdicts);
219976
+ const minPoints = input.minPoints;
219977
+ const minAxes = Math.max(1, Math.floor(input.minAxes));
219978
+ const round = input.round ?? 0;
219979
+ const medians = {};
219980
+ for (const proposal of proposals) medians[proposal] = median(input.verdicts.map((verdict) => verdict.scores[proposal] ?? 0));
219981
+ const ranked = [...proposals].toSorted((left, right) => {
219982
+ const delta = (medians[right] ?? 0) - (medians[left] ?? 0);
219983
+ return delta === 0 ? left.localeCompare(right) : delta;
219984
+ });
219985
+ const winner = ranked[0];
219986
+ const second = ranked[1];
219987
+ if (winner === void 0) throw new Error("no hay propuestas que comparar");
219988
+ if (second === void 0) return {
219989
+ verdict: "refine",
219990
+ winner,
219991
+ second: void 0,
219992
+ medians,
219993
+ medianMargin: 0,
219994
+ judgesHoldingMargin: 0,
219995
+ perJudge: [],
219996
+ reason: `sin comparacion no hay quorum: solo hay una propuesta (${winner})`
219997
+ };
219998
+ const winnerMedian = medians[winner] ?? 0;
219999
+ const secondMedian = medians[second] ?? 0;
220000
+ const medianMargin = Number((winnerMedian - secondMedian).toFixed(6));
220001
+ if (medianMargin === 0) return {
220002
+ verdict: round >= 2 ? "deadlock" : "refine",
220003
+ winner: void 0,
220004
+ second,
220005
+ medians,
220006
+ medianMargin,
220007
+ judgesHoldingMargin: 0,
220008
+ perJudge: [],
220009
+ reason: `empate en la mediana entre ${winner} y ${second}: no hay ganador`
220010
+ };
220011
+ const perJudge = input.verdicts.map((verdict) => {
220012
+ const margin = (verdict.scores[winner] ?? 0) - (verdict.scores[second] ?? 0);
220013
+ return {
220014
+ judge: verdict.judge,
220015
+ margin,
220016
+ holds: margin >= minPoints
220017
+ };
220018
+ });
220019
+ const judgesHoldingMargin = perJudge.filter((entry) => entry.holds).length;
220020
+ const marginOk = medianMargin >= minPoints;
220021
+ const majorityOk = judgesHoldingMargin >= minAxes;
220022
+ if (marginOk && majorityOk) return {
220023
+ verdict: "adopt",
220024
+ winner,
220025
+ second,
220026
+ medians,
220027
+ medianMargin,
220028
+ judgesHoldingMargin,
220029
+ perJudge,
220030
+ reason: `mediana ${winner} ${winnerMedian} vs ${second} ${secondMedian}: margen ${medianMargin} en ${judgesHoldingMargin}/${perJudge.length} jueces`
220031
+ };
220032
+ const reason = [marginOk ? void 0 : `margen ${medianMargin} < ${minPoints}`, majorityOk ? void 0 : `solo ${judgesHoldingMargin}/${perJudge.length} jueces superan el margen (minimo ${minAxes})`].filter((part) => part !== void 0).join(" y ");
220033
+ return {
220034
+ verdict: round >= 2 ? "deadlock" : "refine",
220035
+ winner,
220036
+ second,
220037
+ medians,
220038
+ medianMargin,
220039
+ judgesHoldingMargin,
220040
+ perJudge,
220041
+ reason: `${reason}: sin quorum`
220042
+ };
220043
+ }
220044
+ var init_quorum = __esmMin((() => {}));
220045
+ // ../../packages/agent-core-v2/src/features/wave/tools/decide/decideRunner.ts
220046
+ function runWaveDecideForRoot(input) {
220047
+ const root = resolve(input.root);
220048
+ const config = readWaveConfig(nodeWaveFs, root);
220049
+ if (config === void 0) return {
220050
+ output: `no hay workflow.config.json en ${root}`,
220051
+ isError: true
220052
+ };
220053
+ let result;
220054
+ try {
220055
+ result = evaluateQuorum({
220056
+ verdicts: input.verdicts,
220057
+ minPoints: input.minPoints ?? config.quorum.minPoints,
220058
+ minAxes: input.minAxes ?? config.quorum.minAxes,
220059
+ round: input.round
220060
+ });
220061
+ } catch (error) {
220062
+ return {
220063
+ output: `veredictos invalidos: ${error instanceof Error ? error.message : String(error)}`,
220064
+ isError: true
220065
+ };
220066
+ }
220067
+ const holding = result.perJudge.length === 0 ? "sin comparacion" : `${result.judgesHoldingMargin}/${result.perJudge.length}`;
220068
+ const lines = [
220069
+ `QUORUM — ${result.verdict}`,
220070
+ "",
220071
+ ...Object.entries(result.medians).map(([proposal, value]) => ` mediana ${proposal}: ${value}`),
220072
+ "",
220073
+ ` ganador: ${result.winner ?? "(empate)"}`,
220074
+ ` margen: ${result.medianMargin} (minimo ${input.minPoints ?? config.quorum.minPoints})`,
220075
+ ` jueces con margen: ${holding} (minimo ${input.minAxes ?? config.quorum.minAxes})`,
220076
+ ...result.perJudge.map((entry) => ` ${entry.judge}: ${entry.margin}${entry.holds ? " ok" : " NO"}`),
220077
+ ...result.perJudge.filter((entry) => entry.margin < 0).map((entry) => ` disidencia: ${entry.judge} ${entry.margin}`),
220078
+ "",
220079
+ ` motivo: ${result.reason}`
220080
+ ];
220081
+ if (result.verdict === "deadlock") lines.push("", " dos rondas sin quorum: adopta R1 con disidencia escrita, nunca la presentes como unanime");
220082
+ return {
220083
+ output: lines.join("\n"),
220084
+ isError: result.verdict !== "adopt"
220085
+ };
220086
+ }
220087
+ var init_decideRunner = __esmMin((() => {
220088
+ init_config$6();
220089
+ init_nodeFs();
220090
+ init_quorum();
220203
220091
  }));
220204
- // ../../packages/agent-core-v2/src/features/wave/tools/submit/submitTool.ts
220205
- var WaveSubmitTool;
220206
- var init_submitTool = __esmMin((() => {
220092
+ // ../../packages/agent-core-v2/src/features/wave/tools/decide/decide.md?raw
220093
+ var decide_default;
220094
+ var init_decide = __esmMin((() => {
220095
+ decide_default = "Compute the wave quorum from the judges' totals and say what may happen next.\n\nPass one entry per judge with its totals per proposal (`{ judge: \"J1\", scores: { P1: 17, P2: 15, P3: 14 } }`). The tool takes the **median** per proposal and applies both conditions of `core/quorum.ts`: the winner's median must beat the second's by `minPoints` (**margen de medianas**), and at least `minAxes` judges must give the winner that margin or more over the second (**mayoría de jueces**). Thresholds default to `workflow.config.json`.\n\nAnswers `adopt`, `refine` (no quorum yet, compose R1 and audit with R2) or `deadlock` (two rounds without quorum: adopt R1 with written dissent). A tie in the median, or a single proposal with nothing to compare, never crowns a winner and never counts as quorum.\n";
220096
+ }));
220097
+ // ../../packages/agent-core-v2/src/features/wave/tools/decide/decideTool.ts
220098
+ var WaveDecideTool;
220099
+ var init_decideTool = __esmMin((() => {
220207
220100
  init_sessionContext();
220208
220101
  init_input_schema();
220209
- init_submit$1();
220210
- init_submitRunner();
220211
- init_submit();
220102
+ init_decide$1();
220103
+ init_decideRunner();
220104
+ init_decide();
220212
220105
  init_decorateParam();
220213
220106
  init_decorate();
220214
- WaveSubmitTool = class WaveSubmitTool {
220107
+ WaveDecideTool = class WaveDecideTool {
220215
220108
  sessionContext;
220216
- name = "WaveSubmit";
220217
- description = submit_default;
220218
- parameters = toInputJsonSchema(WaveSubmitToolInputSchema);
220109
+ name = "WaveDecide";
220110
+ description = decide_default;
220111
+ parameters = toInputJsonSchema(WaveDecideToolInputSchema);
220219
220112
  constructor(sessionContext) {
220220
220113
  this.sessionContext = sessionContext;
220221
220114
  }
220222
220115
  resolveExecution(args) {
220223
220116
  return {
220224
- description: `Submitting ${args.role}`,
220117
+ description: "Computing the wave quorum",
220225
220118
  approvalRule: this.name,
220226
- execute: () => Promise.resolve(runWaveSubmitForRoot({
220119
+ execute: () => Promise.resolve(runWaveDecideForRoot({
220227
220120
  root: args.root ?? this.sessionContext.cwd,
220228
- wave: args.wave,
220229
- round: args.round,
220230
- role: args.role,
220231
- artifact: args.artifact,
220232
- ticket: args.ticket,
220233
- reason: args.reason,
220234
- replace: args.replace
220121
+ verdicts: args.verdicts,
220122
+ minPoints: args.minPoints,
220123
+ minAxes: args.minAxes,
220124
+ round: args.round
220235
220125
  }))
220236
220126
  };
220237
220127
  }
220238
220128
  };
220239
- WaveSubmitTool = __decorate([__decorateParam(0, ISessionContext)], WaveSubmitTool);
220129
+ WaveDecideTool = __decorate([__decorateParam(0, ISessionContext)], WaveDecideTool);
220130
+ }));
220131
+ // ../../packages/agent-core-v2/src/features/wave/tools/gate/gate.ts
220132
+ var WaveGateToolInputSchema, IWaveGateTool;
220133
+ var init_gate$1 = __esmMin((() => {
220134
+ init_zod$1();
220135
+ init_instantiation();
220136
+ WaveGateToolInputSchema = object$1({
220137
+ root: string().optional(),
220138
+ json: boolean$1().optional()
220139
+ }).strict();
220140
+ IWaveGateTool = createDecorator("waveGateTool");
220240
220141
  }));
220241
220142
  // ../../packages/agent-core-v2/src/features/wave/core/verify/runner.ts
220242
220143
  function splitCommand(command) {
@@ -220881,7 +220782,7 @@ function runWaveGate(fs, root, cfg) {
220881
220782
  }
220882
220783
  });
220883
220784
  block("brief", () => {
220884
- const templateFile = join(root, BRIEF_TEMPLATE);
220785
+ const templateFile = join(root, BRIEF_TEMPLATE$1);
220885
220786
  if (!fs.exists(templateFile)) return;
220886
220787
  const template = briefSections(fs.read(templateFile) ?? "");
220887
220788
  if (wavesDir === void 0 || !fs.isDirectory(wavesDir)) return;
@@ -220893,11 +220794,11 @@ function runWaveGate(fs, root, cfg) {
220893
220794
  if (text === void 0) continue;
220894
220795
  const at = relative$1(root, file);
220895
220796
  const sections = briefSections(text);
220896
- if (sections.length !== template.length) add("waves.brief_template_drift", at, `${sections.length} secciones frente a ${template.length} de ${BRIEF_TEMPLATE}: el brief ya no sigue la plantilla`);
220797
+ if (sections.length !== template.length) add("waves.brief_template_drift", at, `${sections.length} secciones frente a ${template.length} de ${BRIEF_TEMPLATE$1}: el brief ya no sigue la plantilla`);
220897
220798
  for (const [index, expected] of template.entries()) {
220898
220799
  const section = sections[index];
220899
220800
  if (section === void 0) continue;
220900
- if (sectionKeyword(section.title) !== sectionKeyword(expected.title)) add("waves.brief_template_drift", at, `la seccion ${index + 1} se titula "${section.title}" y ${BRIEF_TEMPLATE} exige "${expected.title}"`);
220801
+ if (sectionKeyword(section.title) !== sectionKeyword(expected.title)) add("waves.brief_template_drift", at, `la seccion ${index + 1} se titula "${section.title}" y ${BRIEF_TEMPLATE$1} exige "${expected.title}"`);
220901
220802
  }
220902
220803
  for (const section of sections) if (section.body === "") add("waves.brief_empty_section", at, `la seccion "${section.title}" no tiene contenido`);
220903
220804
  }
@@ -221186,6 +221087,19 @@ function runWaveGate(fs, root, cfg) {
221186
221087
  }
221187
221088
  });
221188
221089
  block("hygiene", () => {
221090
+ const wavesDir = join(root, cfg.layout.waves);
221091
+ for (const wave of cfg.waves) {
221092
+ const dir = fs.list(wavesDir).find((entry) => entry === wave || entry.startsWith(`${wave}-`));
221093
+ if (dir === void 0) continue;
221094
+ const contracted = contractedSeats(fs, join(wavesDir, dir, WAVE_CONTRACTS_FILE));
221095
+ if (contracted === void 0) continue;
221096
+ for (const round of WAVE_ROUNDS) for (const role of waveRoundSeats(cfg.roles, round)) {
221097
+ const path = `${cfg.layout.waves}/${dir}/${WAVE_ROUND_DIRS[round]}/${role}.md`;
221098
+ if (!fs.exists(join(root, path))) continue;
221099
+ if (contracted.some((seat) => seat.round === round && seat.role === role)) continue;
221100
+ add("contracts.uncontracted", path, `el asiento ${role} de ${round} entrego sin contrato de WaveContract`);
221101
+ }
221102
+ }
221189
221103
  const secrets = cfg.secrets.filter((secret) => secret.length >= 6);
221190
221104
  if (secrets.length === 0) return;
221191
221105
  const files = [...walk(fs, join(root, cfg.layout.waves), (path) => /\.(md|json|txt)$/.test(path)), ...walk(fs, join(root, cfg.layout.backlog), (path) => /\.(md|json|txt)$/.test(path))];
@@ -221207,12 +221121,13 @@ function runWaveGate(fs, root, cfg) {
221207
221121
  }
221208
221122
  };
221209
221123
  }
221210
- var DECISION_FILE, BRIEF_TEMPLATE, AC_FIELDS, OPERATOR_WITH_NUMBER, EXACT_VALUE, ASSERTION, escapeRegExp$1, NOT_RUN_BLOCKS, normalize$1, relative$1, DECISION_SEPARATOR, DECISION_PROPOSAL, DECISION_LEADING_NUMBER, DECISION_JUDGE, DECISION_SIGNED_MARGIN;
221124
+ var DECISION_FILE, BRIEF_TEMPLATE$1, AC_FIELDS, OPERATOR_WITH_NUMBER, EXACT_VALUE, ASSERTION, escapeRegExp$1, NOT_RUN_BLOCKS, normalize$1, relative$1, DECISION_SEPARATOR, DECISION_PROPOSAL, DECISION_LEADING_NUMBER, DECISION_JUDGE, DECISION_SIGNED_MARGIN;
221211
221125
  var init_checks = __esmMin((() => {
221212
221126
  init_config$6();
221127
+ init_seats();
221213
221128
  init_runner();
221214
221129
  DECISION_FILE = "06-DECISION.md";
221215
- BRIEF_TEMPLATE = "workflow/templates/brief.md";
221130
+ BRIEF_TEMPLATE$1 = "workflow/templates/brief.md";
221216
221131
  AC_FIELDS = [
221217
221132
  "id",
221218
221133
  "given",
@@ -221314,6 +221229,290 @@ var init_gateTool = __esmMin((() => {
221314
221229
  };
221315
221230
  WaveGateTool = __decorate([__decorateParam(0, ISessionContext)], WaveGateTool);
221316
221231
  }));
221232
+ // ../../packages/agent-core-v2/src/features/wave/tools/open/open.ts
221233
+ var WaveOpenToolInputSchema, IWaveOpenTool;
221234
+ var init_open$1 = __esmMin((() => {
221235
+ init_zod$1();
221236
+ init_instantiation();
221237
+ WaveOpenToolInputSchema = object$1({
221238
+ name: string().min(2),
221239
+ id: string().optional(),
221240
+ root: string().optional()
221241
+ }).strict();
221242
+ IWaveOpenTool = createDecorator("waveOpenTool");
221243
+ }));
221244
+ // ../../packages/agent-core-v2/src/features/wave/tools/open/openRunner.ts
221245
+ function briefTemplate(root) {
221246
+ const fs = nodeWaveFs;
221247
+ const local = join(root, BRIEF_TEMPLATE);
221248
+ if (fs.exists(local)) return { file: local };
221249
+ if (fs.isDirectory(join(root, "workflow"))) return { missing: BRIEF_TEMPLATE };
221250
+ return fs.exists(BUNDLED_BRIEF_TEMPLATE) ? { file: BUNDLED_BRIEF_TEMPLATE } : { missing: BRIEF_TEMPLATE };
221251
+ }
221252
+ function nextEpic(backlogDir) {
221253
+ const numbers = asArray$2(asRecord$2(readJson$2(nodeWaveFs, join(backlogDir, "epics.json")) ?? {})["epics"]).map((epic) => /^E(\d{2,})$/.exec(String(asRecord$2(epic)["id"]))?.[1]).filter((value) => value !== void 0).map(Number);
221254
+ const next = numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
221255
+ return `E${String(next).padStart(2, "0")}`;
221256
+ }
221257
+ function slugify(name) {
221258
+ return name.normalize("NFD").replaceAll(/[\u0300-\u036F]/g, "").toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "");
221259
+ }
221260
+ function runWaveOpenForRoot(input) {
221261
+ const fs = nodeWaveFs;
221262
+ const configFile = join(input.root, "workflow.config.json");
221263
+ const rawConfig = readJson$2(fs, configFile);
221264
+ if (rawConfig === void 0) return {
221265
+ output: `no hay workflow.config.json en ${input.root}`,
221266
+ isError: true
221267
+ };
221268
+ const layout = asRecord$2(asRecord$2(rawConfig)["layout"]);
221269
+ const wavesRel = typeof layout["waves"] === "string" ? layout["waves"] : "design/waves";
221270
+ const backlogRel = typeof layout["backlog"] === "string" ? layout["backlog"] : "backlog";
221271
+ const minimum = makeWaveConfig(rawConfig).thresholds.minBriefSections;
221272
+ let wavesDir;
221273
+ let backlogDir;
221274
+ try {
221275
+ wavesDir = containedPath(input.root, wavesRel);
221276
+ backlogDir = containedPath(input.root, backlogRel);
221277
+ } catch (error) {
221278
+ return {
221279
+ output: error instanceof Error ? error.message : String(error),
221280
+ isError: true
221281
+ };
221282
+ }
221283
+ const template = briefTemplate(input.root);
221284
+ if (template.file === void 0) return {
221285
+ output: `falta ${template.missing ?? BRIEF_TEMPLATE}: no se genera un brief sin la plantilla unica`,
221286
+ isError: true
221287
+ };
221288
+ const source = fs.read(template.file);
221289
+ if (source === void 0) return {
221290
+ output: `no se pudo leer ${template.file}`,
221291
+ isError: true
221292
+ };
221293
+ const existing = fs.list(wavesDir).filter((name) => fs.isDirectory(join(wavesDir, name))).map((name) => Number(/^W(\d+)/.exec(name)?.[1] ?? NaN)).filter((value) => !Number.isNaN(value));
221294
+ const next = input.id ?? `W${existing.length > 0 ? Math.max(...existing) + 1 : 0}`;
221295
+ if (!/^W\d{1,2}$/.test(next)) return {
221296
+ output: `id de oleada invalido "${next}"`,
221297
+ isError: true
221298
+ };
221299
+ const slug = slugify(input.name);
221300
+ if (slug === "") return {
221301
+ output: "el nombre de la oleada queda vacio al normalizarlo",
221302
+ isError: true
221303
+ };
221304
+ if (fs.list(wavesDir).some((name) => name === next || name.startsWith(`${next}-`))) return {
221305
+ output: `la oleada ${next} ya existe`,
221306
+ isError: true
221307
+ };
221308
+ const dir = join(wavesDir, `${next}-${slug}`);
221309
+ const brief = source.replaceAll("{{WAVE}}", next).replaceAll("{{NAME}}", slug).replaceAll("{{NEXT_EPIC}}", nextEpic(backlogDir));
221310
+ for (const sub of SUBDIRS) fs.mkdir(join(dir, sub));
221311
+ fs.write(join(dir, "00-brief.md"), brief);
221312
+ const declared = asArray$2(asRecord$2(rawConfig)["waves"]).map(String);
221313
+ if (!declared.includes(next)) writeJson(fs, configFile, {
221314
+ ...asRecord$2(rawConfig),
221315
+ waves: [...declared, next].toSorted()
221316
+ });
221317
+ return { output: [
221318
+ `oleada creada: ${next}-${slug}`,
221319
+ ` brief: ${wavesRel}/${next}-${slug}/00-brief.md (rellena las ${minimum} secciones antes de lanzar asientos)`,
221320
+ ` config: ${next} declarada en workflow.config.json (el gate la exige desde ahora)`
221321
+ ].join("\n") };
221322
+ }
221323
+ var SUBDIRS, BRIEF_TEMPLATE, BUNDLED_BRIEF_TEMPLATE;
221324
+ var init_openRunner = __esmMin((() => {
221325
+ init_config$6();
221326
+ init_fsJson();
221327
+ init_nodeFs();
221328
+ SUBDIRS = [
221329
+ "01-proposals",
221330
+ "02-defenses",
221331
+ "03-attacks",
221332
+ "04-verdicts",
221333
+ "05-refined"
221334
+ ];
221335
+ BRIEF_TEMPLATE = "workflow/templates/brief.md";
221336
+ BUNDLED_BRIEF_TEMPLATE = resolve(import.meta.dirname, "../../../../../../../workflow/templates/brief.md");
221337
+ }));
221338
+ // ../../packages/agent-core-v2/src/features/wave/tools/open/open.md?raw
221339
+ var open_default;
221340
+ var init_open = __esmMin((() => {
221341
+ open_default = "Open a wave: create its artifact tree and a brief skeleton, and declare the wave in `workflow.config.json`.\n\nThe brief starts with the seven mandatory sections and nothing else — a brief that already contains the answer makes the twelve seats theatre. Fill it before launching any seat; the gate rejects a brief with fewer than seven sections.\n\nOpening a wave also declares it in the config, so the package gate will require its artifacts from then on.\n";
221342
+ }));
221343
+ // ../../packages/agent-core-v2/src/features/wave/tools/open/openTool.ts
221344
+ var WaveOpenTool;
221345
+ var init_openTool = __esmMin((() => {
221346
+ init_sessionContext();
221347
+ init_input_schema();
221348
+ init_open$1();
221349
+ init_openRunner();
221350
+ init_open();
221351
+ init_decorateParam();
221352
+ init_decorate();
221353
+ WaveOpenTool = class WaveOpenTool {
221354
+ sessionContext;
221355
+ name = "WaveOpen";
221356
+ description = open_default;
221357
+ parameters = toInputJsonSchema(WaveOpenToolInputSchema);
221358
+ constructor(sessionContext) {
221359
+ this.sessionContext = sessionContext;
221360
+ }
221361
+ resolveExecution(args) {
221362
+ return {
221363
+ description: `Opening wave ${args.name}`,
221364
+ approvalRule: this.name,
221365
+ execute: () => Promise.resolve(runWaveOpenForRoot({
221366
+ root: args.root ?? this.sessionContext.cwd,
221367
+ name: args.name,
221368
+ id: args.id
221369
+ }))
221370
+ };
221371
+ }
221372
+ };
221373
+ WaveOpenTool = __decorate([__decorateParam(0, ISessionContext)], WaveOpenTool);
221374
+ }));
221375
+ // ../../packages/agent-core-v2/src/features/wave/tools/submit/submit.ts
221376
+ var WaveSubmitToolInputSchema, IWaveSubmitTool;
221377
+ var init_submit$1 = __esmMin((() => {
221378
+ init_zod$1();
221379
+ init_instantiation();
221380
+ WaveSubmitToolInputSchema = object$1({
221381
+ wave: string().min(2),
221382
+ round: _enum([
221383
+ "propose",
221384
+ "defend",
221385
+ "attack",
221386
+ "judge",
221387
+ "refine",
221388
+ "audit"
221389
+ ]),
221390
+ role: string().min(2),
221391
+ artifact: string().min(1),
221392
+ ticket: string().optional(),
221393
+ reason: string().optional(),
221394
+ replace: boolean$1().optional(),
221395
+ root: string().optional()
221396
+ }).strict();
221397
+ IWaveSubmitTool = createDecorator("waveSubmitTool");
221398
+ }));
221399
+ // ../../packages/agent-core-v2/src/features/wave/tools/submit/submitRunner.ts
221400
+ function findWaveDir(root, wavesRel, wave) {
221401
+ const fs = nodeWaveFs;
221402
+ const wavesDir = containedPath(root, wavesRel);
221403
+ const name = fs.list(wavesDir).filter((entry) => fs.isDirectory(join(wavesDir, entry))).find((entry) => entry === wave || entry.startsWith(`${wave}-`));
221404
+ return name === void 0 ? void 0 : join(wavesDir, name);
221405
+ }
221406
+ function runWaveSubmitForRoot(input) {
221407
+ const fs = nodeWaveFs;
221408
+ const rawConfig = readJson$2(fs, join(input.root, "workflow.config.json"));
221409
+ if (rawConfig === void 0) return {
221410
+ output: `no hay workflow.config.json en ${input.root}`,
221411
+ isError: true
221412
+ };
221413
+ const config = makeWaveConfig(rawConfig);
221414
+ const wavesRel = config.layout.waves;
221415
+ const seats = waveRoundSeats(config.roles, input.round);
221416
+ if (!seats.includes(input.role)) return {
221417
+ output: `seats.unknown_seat: el rol "${input.role}" no pertenece a la ronda ${input.round} (esperado ${seats.join(", ")})`,
221418
+ isError: true
221419
+ };
221420
+ let base;
221421
+ try {
221422
+ base = findWaveDir(input.root, wavesRel, input.wave);
221423
+ } catch (error) {
221424
+ return {
221425
+ output: error instanceof Error ? error.message : String(error),
221426
+ isError: true
221427
+ };
221428
+ }
221429
+ if (base === void 0) return {
221430
+ output: `no existe la oleada ${input.wave} en ${wavesRel}`,
221431
+ isError: true
221432
+ };
221433
+ const dirName = WAVE_ROUND_DIRS[input.round];
221434
+ const dir = join(base, dirName);
221435
+ if (!fs.isDirectory(dir)) return {
221436
+ output: `falta el directorio ${dirName} en la oleada ${input.wave}`,
221437
+ isError: true
221438
+ };
221439
+ const seatsDir = join(base, WAVE_SEATS_DIR);
221440
+ if (fs.isDirectory(seatsDir) && fs.list(seatsDir).length > 0) {
221441
+ const presented = (input.ticket ?? "").trim();
221442
+ if (presented === "") return {
221443
+ output: `seats.foreign_replace: el asiento ${input.role} no presento el ticket que emitio la ronda`,
221444
+ isError: true
221445
+ };
221446
+ const stored = (fs.read(join(seatsDir, input.role)) ?? "").trim();
221447
+ if (stored === "" || stored !== seatTicketHash(presented)) return {
221448
+ output: `seats.foreign_replace: el ticket presentado no es el del asiento ${input.role}`,
221449
+ isError: true
221450
+ };
221451
+ if (input.replace === true && (input.reason ?? "").trim() === "") return {
221452
+ output: `seats.replace_without_reason: reescribir el artefacto del asiento ${input.role} exige un motivo no vacio`,
221453
+ isError: true
221454
+ };
221455
+ }
221456
+ const file = join(dir, `${input.role}.md`);
221457
+ if (fs.exists(file) && input.replace !== true) return {
221458
+ output: `el asiento ${input.role} ya entrego su artefacto; un asiento escribe una vez (usa replace solo para relanzar un fallo)`,
221459
+ isError: true
221460
+ };
221461
+ if (input.artifact.trim() === "") return {
221462
+ output: "el artefacto esta vacio: un asiento que no escribe, fallo",
221463
+ isError: true
221464
+ };
221465
+ fs.write(file, input.artifact.endsWith("\n") ? input.artifact : `${input.artifact}\n`);
221466
+ return { output: `escrito ${wavesRel}/${base.split("/").pop()}/${dirName}/${input.role}.md` };
221467
+ }
221468
+ var init_submitRunner = __esmMin((() => {
221469
+ init_config$6();
221470
+ init_fsJson();
221471
+ init_nodeFs();
221472
+ init_seats();
221473
+ }));
221474
+ // ../../packages/agent-core-v2/src/features/wave/tools/submit/submit.md?raw
221475
+ var submit_default;
221476
+ var init_submit = __esmMin((() => {
221477
+ submit_default = "Deliver the artifact for your seat. This is the only way a seat writes: the tool binds the role to its slot and refuses anything else.\n\nRole and round must match — `propose` accepts P1..Pn, `defend` D1..Dn, `attack` A1..An, `judge` J1..Jn, `refine` R1, `audit` R2..Rn. A seat writes once; use `replace` only to relaunch a failed seat after its artifact was already written.\n\nPass the `ticket` the round gave you: the tool hashes it and compares it with the sha256 the round left in `design/waves/<W>/.seats/<role>`. A missing ticket, or the ticket of another seat, is refused as `seats.foreign_replace`. The ticket is capacity, not identity (capacidad, no identidad): it proves the orchestrator issued this seat, it does not say who wrote the artifact. `replace` also needs a non-empty `reason`, or the tool refuses it as `seats.replace_without_reason`: a silent overwrite is the substitution this tool exists to prevent.\n\nPass the artifact as the text/JSON body. An empty artifact is rejected: a seat that did not write has failed, and the round must be relaunched rather than summarized by the orchestrator.\n";
221478
+ }));
221479
+ // ../../packages/agent-core-v2/src/features/wave/tools/submit/submitTool.ts
221480
+ var WaveSubmitTool;
221481
+ var init_submitTool = __esmMin((() => {
221482
+ init_sessionContext();
221483
+ init_input_schema();
221484
+ init_submit$1();
221485
+ init_submitRunner();
221486
+ init_submit();
221487
+ init_decorateParam();
221488
+ init_decorate();
221489
+ WaveSubmitTool = class WaveSubmitTool {
221490
+ sessionContext;
221491
+ name = "WaveSubmit";
221492
+ description = submit_default;
221493
+ parameters = toInputJsonSchema(WaveSubmitToolInputSchema);
221494
+ constructor(sessionContext) {
221495
+ this.sessionContext = sessionContext;
221496
+ }
221497
+ resolveExecution(args) {
221498
+ return {
221499
+ description: `Submitting ${args.role}`,
221500
+ approvalRule: this.name,
221501
+ execute: () => Promise.resolve(runWaveSubmitForRoot({
221502
+ root: args.root ?? this.sessionContext.cwd,
221503
+ wave: args.wave,
221504
+ round: args.round,
221505
+ role: args.role,
221506
+ artifact: args.artifact,
221507
+ ticket: args.ticket,
221508
+ reason: args.reason,
221509
+ replace: args.replace
221510
+ }))
221511
+ };
221512
+ }
221513
+ };
221514
+ WaveSubmitTool = __decorate([__decorateParam(0, ISessionContext)], WaveSubmitTool);
221515
+ }));
221317
221516
  // ../../packages/agent-core-v2/src/features/wave/tools/verify/verify.ts
221318
221517
  var WaveVerifyToolInputSchema, IWaveVerifyTool;
221319
221518
  var init_verify$1 = __esmMin((() => {
@@ -221428,28 +221627,35 @@ var init_verifyTool = __esmMin((() => {
221428
221627
  WaveVerifyTool = __decorate([__decorateParam(0, ISessionContext)], WaveVerifyTool);
221429
221628
  }));
221430
221629
  // ../../packages/agent-core-v2/src/features/wave/waveFeature.ts
221630
+ function profileHolds(accessor, profile) {
221631
+ return accessor.get(IAgentProfileService).data().profileName === profile;
221632
+ }
221431
221633
  function isWaveFeatureAssembled(flags) {
221432
221634
  return assembledOverrideForTests ?? assembledFlagServices.has(flags);
221433
221635
  }
221434
221636
  var WAVE_TOOL_CONTRIBUTIONS, WaveFeature, assembledFlagServices, assembledOverrideForTests;
221435
221637
  var init_waveFeature = __esmMin((() => {
221638
+ init_instantiation();
221639
+ init_profile$1();
221436
221640
  init_flag$7();
221437
221641
  init_feature();
221438
221642
  init_featureRegistry();
221439
- init_wave$3();
221643
+ init_waveService();
221644
+ init_waveMode();
221440
221645
  init_seatProfile();
221646
+ init_contract$4();
221647
+ init_contractTool();
221441
221648
  init_decide$1();
221442
221649
  init_decideTool();
221443
221650
  init_gate$1();
221651
+ init_gateTool();
221444
221652
  init_open$1();
221445
221653
  init_openTool();
221446
- init_round$1();
221447
- init_roundTool();
221448
221654
  init_submit$1();
221449
221655
  init_submitTool();
221450
- init_gateTool();
221451
221656
  init_verify$1();
221452
221657
  init_verifyTool();
221658
+ init_wave$3();
221453
221659
  init_decorateParam();
221454
221660
  init_decorate();
221455
221661
  WAVE_TOOL_CONTRIBUTIONS = [
@@ -221473,15 +221679,15 @@ var init_waveFeature = __esmMin((() => {
221473
221679
  ctor: WaveOpenTool,
221474
221680
  name: "WaveOpen"
221475
221681
  },
221682
+ {
221683
+ id: IWaveContractTool,
221684
+ ctor: WaveContractTool,
221685
+ name: "WaveContract"
221686
+ },
221476
221687
  {
221477
221688
  id: IWaveSubmitTool,
221478
221689
  ctor: WaveSubmitTool,
221479
221690
  name: "WaveSubmit"
221480
- },
221481
- {
221482
- id: IWaveRoundTool,
221483
- ctor: WaveRoundTool,
221484
- name: "WaveRound"
221485
221691
  }
221486
221692
  ];
221487
221693
  WaveFeature = class WaveFeature extends Feature {
@@ -221493,10 +221699,15 @@ var init_waveFeature = __esmMin((() => {
221493
221699
  this.onDispose(() => {
221494
221700
  assembledFlagServices.delete(flags);
221495
221701
  });
221496
- for (const tool of WAVE_TOOL_CONTRIBUTIONS) this.contributeTool(tool.id, tool.ctor, {
221497
- name: tool.name,
221498
- domain: WAVE_TOOL_DOMAIN
221499
- });
221702
+ this.contributeAgentService(IAgentWaveService, AgentWaveService, { activation: 0 });
221703
+ for (const tool of WAVE_TOOL_CONTRIBUTIONS) {
221704
+ const profile = tool.name === "WaveSubmit" ? WAVE_SEAT_PROFILE : WAVE_DIRECTOR_PROFILE;
221705
+ this.contributeTool(tool.id, tool.ctor, {
221706
+ name: tool.name,
221707
+ domain: WAVE_TOOL_DOMAIN,
221708
+ when: (accessor) => profileHolds(accessor, profile)
221709
+ });
221710
+ }
221500
221711
  this.contributeProfiles([WAVE_SEAT_PROFILE_DEF]);
221501
221712
  }
221502
221713
  };
@@ -221521,6 +221732,27 @@ var init_flag$2 = __esmMin((() => {
221521
221732
  };
221522
221733
  registerFlagDefinition(waveFlag);
221523
221734
  }));
221735
+ var init_round = __esmMin((() => {
221736
+ init_zod$1();
221737
+ init_instantiation();
221738
+ object$1({
221739
+ wave: string().min(2),
221740
+ round: _enum([
221741
+ "propose",
221742
+ "defend",
221743
+ "attack",
221744
+ "judge",
221745
+ "refine",
221746
+ "audit"
221747
+ ]),
221748
+ root: string().optional()
221749
+ }).strict();
221750
+ createDecorator("waveRoundTool");
221751
+ }));
221752
+ // ../../packages/agent-core-v2/src/features/wave/tools/round/roundRunner.ts
221753
+ var init_roundRunner = __esmMin((() => {
221754
+ init_prompts$2();
221755
+ }));
221524
221756
  // ../../packages/agent-core-v2/src/agent/usage/cacheProbe.ts
221525
221757
  var IAgentCacheProbeService;
221526
221758
  var init_cacheProbe = __esmMin((() => {
@@ -224085,6 +224317,7 @@ var AGENT_TOOLS, CODER_TOOLS, EXPLORE_TOOLS, CODER_ROLE;
224085
224317
  var init_profiles = __esmMin((() => {
224086
224318
  init_gitContext();
224087
224319
  init_contribution();
224320
+ init_wave$3();
224088
224321
  init_profile_shared();
224089
224322
  init_explore_overlay();
224090
224323
  AGENT_TOOLS = [
@@ -224119,6 +224352,8 @@ var init_profiles = __esmMin((() => {
224119
224352
  "TowerStatus",
224120
224353
  "TowerTeardown",
224121
224354
  "WaveOpen",
224355
+ "WaveRound",
224356
+ "WaveContract",
224122
224357
  "WaveSubmit",
224123
224358
  "WaveGate",
224124
224359
  "WaveDecide",
@@ -224165,7 +224400,8 @@ var init_profiles = __esmMin((() => {
224165
224400
  subagents: [
224166
224401
  "coder",
224167
224402
  "explore",
224168
- "plan"
224403
+ "plan",
224404
+ WAVE_SEAT_PROFILE
224169
224405
  ],
224170
224406
  renderSystemPrompt: (context) => renderSystemPromptResult("", context, { skillActive: skillActiveFor(AGENT_TOOLS) })
224171
224407
  });
@@ -282337,6 +282573,8 @@ var init_src$10 = __esmMin((() => {
282337
282573
  init_flag$2();
282338
282574
  init_wave$3();
282339
282575
  init_waveFeature();
282576
+ init_waveMode();
282577
+ init_waveOps();
282340
282578
  init_gate$1();
282341
282579
  init_gateRunner();
282342
282580
  init_decide$1();
@@ -282348,9 +282586,12 @@ var init_src$10 = __esmMin((() => {
282348
282586
  init_submit$1();
282349
282587
  init_submitRunner();
282350
282588
  init_seatProfile();
282351
- init_round$1();
282589
+ init_round();
282352
282590
  init_roundRunner();
282353
282591
  init_prompts$2();
282592
+ init_seats();
282593
+ init_contract$4();
282594
+ init_contractRunner();
282354
282595
  init_flag$2();
282355
282596
  init_waveFeature();
282356
282597
  init_usage();
@@ -288686,6 +288927,7 @@ var init_sdk_rpc_client_v2 = __esmMin((() => {
288686
288927
  init_connection_manager();
288687
288928
  init_towerRateLimit();
288688
288929
  init_statusReader();
288930
+ init_waveMode();
288689
288931
  init_configLoader();
288690
288932
  init_fs$1();
288691
288933
  init_appendLogStore$1();
@@ -289971,6 +290213,7 @@ var init_sdk_rpc_client_v2 = __esmMin((() => {
289971
290213
  permission: agent.accessor.get(IAgentPermissionModeService).mode,
289972
290214
  planMode: plan !== null,
289973
290215
  swarmMode: agent.accessor.get(IAgentSwarmService).isActive,
290216
+ waveMode: agent.accessor.get(IAgentWaveService).isActive,
289974
290217
  towerMode: agent.accessor.get(IAgentTowerService).isActive,
289975
290218
  contextTokens,
289976
290219
  maxContextTokens,
@@ -290258,6 +290501,11 @@ var init_sdk_rpc_client_v2 = __esmMin((() => {
290258
290501
  });
290259
290502
  return this.prompt(input);
290260
290503
  }
290504
+ async setWaveMode(input) {
290505
+ const wave = (await this.agentScope(input.sessionId)).accessor.get(IAgentWaveService);
290506
+ if (input.enabled) await wave.enter();
290507
+ else await wave.exit();
290508
+ }
290261
290509
  /** Through the agent scope (`IAgentTowerService.enter` / `.exit`) — no klient facade exists. */
290262
290510
  async setTowerMode(input) {
290263
290511
  const agent = await this.agentScope(input.sessionId);
@@ -395450,11 +395698,9 @@ var init_registry$2 = __esmMin((() => {
395450
395698
  {
395451
395699
  name: "wave",
395452
395700
  aliases: [],
395453
- description: "Run a design wave: brief, adversarial swarm, gate and execution",
395701
+ description: "Turn the wave mode on or off, or open a design wave with an objective",
395454
395702
  priority: 90,
395455
- argumentHint: "<objetivo de la oleada>",
395456
- availability: "idle-only",
395457
- experimentalFlag: "wave"
395703
+ argumentHint: "[on|off] | <objetivo de la oleada>"
395458
395704
  },
395459
395705
  {
395460
395706
  name: "model",
@@ -472337,6 +472583,7 @@ var init_footer = __esmMin((() => {
472337
472583
  if (state.permissionMode === "yolo") modes.push(chalk.hex(colors.warning).bold(PERMISSION_MODE_DISPLAY_NAMES.yolo));
472338
472584
  if (state.planMode) modes.push(chalk.hex(colors.primary).bold("plan"));
472339
472585
  if (state.swarmMode) modes.push(chalk.hex(colors.accent).bold("swarm"));
472586
+ if (state.waveMode) modes.push(chalk.hex(colors.accent).bold("wave"));
472340
472587
  if (state.towerMode) modes.push(chalk.hex(colors.accent).bold("tower"));
472341
472588
  if (modes.length > 0) slots["mode"] = [modes.join(" ")];
472342
472589
  const goalBadge = formatGoalBadge(state.goal, colors, this.goalWallClockMs(state.goal));
@@ -478074,30 +478321,58 @@ var init_swarm = __esmMin((() => {
478074
478321
  SWARM_USAGE_NOTICE = "High usage: Swarm is built for large parallel jobs and can use many times the tokens of a normal task. Start small, and run /swarm off when you're done.";
478075
478322
  }));
478076
478323
  // src/tui/commands/wave.ts
478077
- function buildWavePrompt(args) {
478324
+ function buildWavePrompt(objective) {
478078
478325
  return [
478079
- `Ejecuta una oleada del método workflow para: ${args.trim()}`,
478326
+ `Abre una oleada del método workflow para: ${objective}`,
478080
478327
  "",
478081
- "Reglas de la oleada:",
478082
- "- Usa las tools Wave: `WaveOpen` para abrirla, `WaveRound` para obtener el prompt de cada asiento, `WaveSubmit` para que cada asiento entregue su artefacto, `WaveDecide` para el quórum, `WaveGate` para el paquete y `WaveVerify` para cerrar tasks.",
478083
- "- Lanza un agente por asiento con `Agent` (o `AgentSwarm` si el usuario activó swarm) pasando `subagent_type: \"wave-seat\"` y el prompt que devuelve `WaveRound`. No inventes el prompt ni cambies el rol.",
478084
- "- El asiento es de solo lectura: `Read`, `Glob`, `Grep` y `WaveSubmit`. Nunca escribe con Write/Edit: entrega con `WaveSubmit`, que ata rol y ruta.",
478085
- "- Si el brief no tiene las siete secciones, para y pide que se rellenen; no lo escribas tú.",
478086
- "- Sin `adopt` de `WaveDecide`, lanza `refine` y después `audit`; con dos rondas sin quórum, adopta R1 con disidencia escrita.",
478087
- "- La oleada no está cerrada hasta que `WaveGate` sale en verde. Si sale en rojo, dilo con los códigos de hallazgo."
478328
+ "Reglas:",
478329
+ "- Redacta primero el plan de la oleada (WaveOpen con nombre, alcance, evidencia y rondas) y crea el Goal con completionCriterion \"WaveGate verde sobre la oleada W<n> y WaveVerify 0 en todas sus tasks\".",
478330
+ "- No ejecutes la oleada en este turno: el plan se revisa antes de aprobarse.",
478331
+ "- Tras la aprobación, contrata con WaveContract y lanza un agente `wave-seat` por asiento. Tú no escribes artefactos: WaveSubmit es del asiento.",
478332
+ "- Cierras con WaveDecide, WaveVerify y WaveGate; la oleada no está cerrada hasta que WaveGate sale en verde."
478088
478333
  ].join("\n");
478089
478334
  }
478090
478335
  async function handleWaveCommand(host, args) {
478091
- const objective = args.trim();
478092
- if (objective === "") {
478093
- host.showNotice("Wave", WAVE_USAGE);
478336
+ const session = host.session;
478337
+ if (session === void 0) {
478338
+ host.showError(NO_ACTIVE_SESSION_MESSAGE);
478094
478339
  return;
478095
478340
  }
478096
- host.sendNormalUserInput(buildWavePrompt(objective));
478341
+ const input = args.trim();
478342
+ const command = input.toLowerCase();
478343
+ if (command === "on" || command === "off" || command === "") {
478344
+ await applyWaveMode(host, command === "" ? !host.state.appState.waveMode : command === "on");
478345
+ return;
478346
+ }
478347
+ if (!host.state.appState.waveMode && !await applyWaveMode(host, true)) return;
478348
+ if (!host.state.appState.planMode) await applyPlanMode(host, session, true);
478349
+ host.sendNormalUserInput(buildWavePrompt(input));
478350
+ }
478351
+ async function applyWaveMode(host, enabled) {
478352
+ if (enabled === host.state.appState.waveMode) {
478353
+ host.showStatus(`Wave mode is already ${enabled ? "on" : "off"}.`);
478354
+ return true;
478355
+ }
478356
+ try {
478357
+ await host.requireSession().setWaveMode(enabled);
478358
+ } catch (error) {
478359
+ host.showError(`Failed to ${enabled ? "enable" : "disable"} wave mode: ${formatErrorMessage$2(error)}`);
478360
+ return false;
478361
+ }
478362
+ host.setAppState({ waveMode: enabled });
478363
+ host.showNotice(`Wave mode: ${enabled ? "ON" : "OFF"}`);
478364
+ return true;
478097
478365
  }
478098
- var WAVE_USAGE;
478099
478366
  var init_wave = __esmMin((() => {
478100
- WAVE_USAGE = "Uso: /wave <objetivo de la oleada> — abre una oleada de diseño, lanza el swarm adversarial y cierra con el gate. Requiere NEXGEN_EXPERIMENTAL_WAVE.";
478367
+ init_nexgenk_tui();
478368
+ init_event_payload();
478369
+ init_config();
478370
+ [
478371
+ "Uso:",
478372
+ " /wave enciende o apaga el modo wave",
478373
+ " /wave off lo apaga",
478374
+ " /wave <objetivo> lo enciende y abre el plan de una oleada"
478375
+ ].join("\n");
478101
478376
  }));
478102
478377
  // src/tui/commands/tower.ts
478103
478378
  async function handleTowerCommand(host, args) {
@@ -494813,6 +495088,7 @@ var SessionEventHandler = class {
494813
495088
  }
494814
495089
  if (event.planMode !== void 0) patch.planMode = event.planMode;
494815
495090
  if (event.swarmMode !== void 0) patch.swarmMode = event.swarmMode;
495091
+ if (event.waveMode !== void 0) patch.waveMode = event.waveMode;
494816
495092
  if (event.towerMode !== void 0) patch.towerMode = event.towerMode;
494817
495093
  if (event.permission !== void 0) patch.permissionMode = event.permission;
494818
495094
  if (event.model !== void 0) patch.model = event.model;
@@ -499981,6 +500257,7 @@ function createInitialAppState(input) {
499981
500257
  planMode: input.cliOptions.plan,
499982
500258
  inputMode: "prompt",
499983
500259
  swarmMode: false,
500260
+ waveMode: false,
499984
500261
  towerMode: false,
499985
500262
  thinkingEffort: "off",
499986
500263
  contextUsage: 0,
@@ -501750,6 +502027,7 @@ var NexgenTUI = class {
501750
502027
  permissionMode: status.permission,
501751
502028
  planMode: status.planMode,
501752
502029
  swarmMode: status.swarmMode ?? false,
502030
+ waveMode: status.waveMode ?? false,
501753
502031
  towerMode: status.towerMode ?? false,
501754
502032
  contextTokens: status.contextTokens,
501755
502033
  maxContextTokens: status.maxContextTokens,