@wopr-network/defcon 1.15.0 → 1.16.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.
@@ -7,15 +7,12 @@ export type ClaimResponse = {
7
7
  retry_after_ms: number;
8
8
  message: string;
9
9
  } | {
10
- worker_id?: string;
11
10
  entity_id: string;
12
11
  invocation_id: string;
13
12
  flow: string | null;
14
- stage: string;
15
- agent_role: string | null;
16
- prompt: string;
17
- context: Record<string, unknown> | null;
18
- worker_notice?: string;
13
+ state: string;
14
+ refs: Record<string, unknown> | null;
15
+ artifacts: Record<string, unknown> | null;
19
16
  };
20
17
  export type ReportResponse = {
21
18
  next_action: "continue";
@@ -95,6 +95,7 @@ async function parseSeedAndLoad(json, flowRepo, gateRepo, db) {
95
95
  onEnter: s.onEnter,
96
96
  onExit: s.onExit,
97
97
  retryAfterMs: s.retryAfterMs,
98
+ meta: s.meta,
98
99
  });
99
100
  }
100
101
  const flowTransitions = parsed.transitions.filter((t) => t.flowName === f.name);
@@ -45,6 +45,7 @@ export declare const StateDefinitionSchema: z.ZodObject<{
45
45
  timeout_ms: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
46
46
  }, z.core.$strip>>;
47
47
  retryAfterMs: z.ZodOptional<z.ZodNumber>;
48
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
48
49
  }, z.core.$strip>;
49
50
  export declare const CommandGateSchema: z.ZodObject<{
50
51
  name: z.ZodString;
@@ -165,6 +166,7 @@ export declare const SeedFileSchema: z.ZodObject<{
165
166
  timeout_ms: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
166
167
  }, z.core.$strip>>;
167
168
  retryAfterMs: z.ZodOptional<z.ZodNumber>;
169
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
168
170
  }, z.core.$strip>>;
169
171
  gates: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
170
172
  name: z.ZodString;
@@ -60,6 +60,8 @@ export const StateDefinitionSchema = z.object({
60
60
  onEnter: OnEnterSchema.optional(),
61
61
  onExit: OnExitSchema.optional(),
62
62
  retryAfterMs: z.number().int().min(0).optional(),
63
+ /** Opaque metadata passed through to consumers (e.g. radar). Defcon stores but does not interpret. */
64
+ meta: z.record(z.string(), z.unknown()).optional(),
63
65
  });
64
66
  // Gate: discriminated union on `type`
65
67
  const GateOutcomeSchema = z.object({
@@ -21,8 +21,8 @@ export interface ClaimWorkResult {
21
21
  invocationId: string;
22
22
  flowName: string;
23
23
  stage: string;
24
- prompt: string;
25
- context: Record<string, unknown> | null;
24
+ refs: Record<string, unknown> | null;
25
+ artifacts: Record<string, unknown> | null;
26
26
  }
27
27
  export interface EngineStatus {
28
28
  flows: Record<string, Record<string, number>>;
@@ -579,12 +579,6 @@ export class Engine {
579
579
  this.logger.warn(`[engine] setAffinity failed for entity ${claimed.id} worker ${worker_id} — continuing:`, err);
580
580
  }
581
581
  }
582
- const versionedFlow = await this.flowRepo.getAtVersion(claimed.flowId, claimed.flowVersion);
583
- const effectiveFlow = versionedFlow ?? flow;
584
- const state = effectiveFlow.states.find((s) => s.name === pending.stage);
585
- const build = state
586
- ? await this.buildPromptForEntity(state, claimed, effectiveFlow)
587
- : { prompt: pending.prompt, context: null };
588
582
  await this.eventEmitter.emit({
589
583
  type: "entity.claimed",
590
584
  entityId: claimed.id,
@@ -597,8 +591,8 @@ export class Engine {
597
591
  invocationId: claimedInvocation.id,
598
592
  flowName: flow.name,
599
593
  stage: pending.stage,
600
- prompt: build.prompt,
601
- context: build.context,
594
+ refs: claimed.refs ?? null,
595
+ artifacts: claimed.artifacts ?? null,
602
596
  };
603
597
  }
604
598
  // 8. Fallback: no unclaimed invocations — claim entity directly and create invocation
@@ -676,8 +670,8 @@ export class Engine {
676
670
  invocationId: claimedInvocation.id,
677
671
  flowName: flow.name,
678
672
  stage: state.name,
679
- prompt: build.prompt,
680
- context: build.context,
673
+ refs: claimed.refs ?? null,
674
+ artifacts: claimed.artifacts ?? null,
681
675
  };
682
676
  }
683
677
  }
@@ -173,8 +173,15 @@ export async function handleFlowClaim(deps, args) {
173
173
  continue;
174
174
  }
175
175
  const flow = entity ? flowById.get(entity.flowId) : undefined;
176
+ if (!flow) {
177
+ await deps.entities.release(entity.id, claimerId).catch(() => { });
178
+ await deps.invocations
179
+ .fail(claimed.id, `Flow not found for entity ${entity.id} (flowId: ${entity.flowId})`)
180
+ .catch(() => { });
181
+ continue;
182
+ }
176
183
  // Record affinity for the claiming worker (best-effort; failure must not block the claim)
177
- if (worker_id && entity && flow) {
184
+ if (worker_id && entity) {
178
185
  try {
179
186
  const windowMs = flow.affinityWindowMs ?? 300000;
180
187
  await deps.entities.setAffinity(claimed.entityId, worker_id, role, new Date(Date.now() + windowMs));
@@ -198,14 +205,12 @@ export async function handleFlowClaim(deps, args) {
198
205
  });
199
206
  }
200
207
  return jsonResult({
201
- worker_id: worker_id,
202
- entity_id: claimed.entityId,
208
+ entity_id: entity.id,
203
209
  invocation_id: claimed.id,
204
- flow: flow?.name ?? null,
205
- stage: claimed.stage,
206
- agent_role: claimed.agentRole || null,
207
- prompt: claimed.prompt,
208
- context: claimed.context,
210
+ flow: flow.name,
211
+ state: claimed.stage,
212
+ refs: claimedEntity.refs ?? null,
213
+ artifacts: claimedEntity.artifacts ?? null,
209
214
  });
210
215
  }
211
216
  return noWorkResult(RETRY_SHORT_MS, role);
@@ -17,6 +17,7 @@ function rowToState(r) {
17
17
  onEnter: r.onEnter ?? null,
18
18
  onExit: r.onExit ?? null,
19
19
  retryAfterMs: r.retryAfterMs ?? null,
20
+ meta: r.meta,
20
21
  };
21
22
  }
22
23
  function rowToTransition(r) {
@@ -161,6 +162,7 @@ export class DrizzleFlowRepository {
161
162
  onEnter: s.onEnter ?? null,
162
163
  onExit: s.onExit ?? null,
163
164
  retryAfterMs: s.retryAfterMs ?? null,
165
+ meta: s.meta ?? null,
164
166
  })),
165
167
  transitions: (snap.transitions ?? []).map((t) => ({
166
168
  id: t.id,
@@ -238,6 +240,7 @@ export class DrizzleFlowRepository {
238
240
  onEnter: (state.onEnter ?? null),
239
241
  onExit: (state.onExit ?? null),
240
242
  retryAfterMs: state.retryAfterMs ?? null,
243
+ meta: (state.meta ?? null),
241
244
  };
242
245
  this.db.transaction((tx) => {
243
246
  tx.insert(stateDefinitions).values(row).run();
@@ -268,6 +271,8 @@ export class DrizzleFlowRepository {
268
271
  updateValues.onExit = changes.onExit;
269
272
  if (changes.retryAfterMs !== undefined)
270
273
  updateValues.retryAfterMs = changes.retryAfterMs;
274
+ if (changes.meta !== undefined)
275
+ updateValues.meta = changes.meta;
271
276
  if (Object.keys(updateValues).length > 0) {
272
277
  this.db.update(stateDefinitions).set(updateValues).where(eq(stateDefinitions.id, stateId)).run();
273
278
  }
@@ -532,6 +532,23 @@ export declare const stateDefinitions: import("drizzle-orm/sqlite-core").SQLiteT
532
532
  identity: undefined;
533
533
  generated: undefined;
534
534
  }, {}, {}>;
535
+ meta: import("drizzle-orm/sqlite-core").SQLiteColumn<{
536
+ name: "meta";
537
+ tableName: "state_definitions";
538
+ dataType: "json";
539
+ columnType: "SQLiteTextJson";
540
+ data: unknown;
541
+ driverParam: string;
542
+ notNull: false;
543
+ hasDefault: false;
544
+ isPrimaryKey: false;
545
+ isAutoincrement: false;
546
+ hasRuntimeDefault: false;
547
+ enumValues: undefined;
548
+ baseColumn: never;
549
+ identity: undefined;
550
+ generated: undefined;
551
+ }, {}, {}>;
535
552
  };
536
553
  dialect: "sqlite";
537
554
  }>;
@@ -34,6 +34,8 @@ export const stateDefinitions = sqliteTable("state_definitions", {
34
34
  onEnter: text("on_enter", { mode: "json" }),
35
35
  onExit: text("on_exit", { mode: "json" }),
36
36
  retryAfterMs: integer("retry_after_ms"),
37
+ /** Opaque metadata passed through to consumers. Defcon stores but does not interpret. */
38
+ meta: text("meta", { mode: "json" }),
37
39
  }, (table) => ({
38
40
  flowNameUnique: uniqueIndex("state_definitions_flow_name_unique").on(table.flowId, table.name),
39
41
  }));
@@ -99,6 +99,8 @@ export interface State {
99
99
  onExit: OnExitConfig | null;
100
100
  /** Override check_back delay for workers claiming this state. Falls back to Flow.claimRetryAfterMs. */
101
101
  retryAfterMs: number | null;
102
+ /** Opaque metadata passed through to consumers (e.g. radar). Defcon stores but does not interpret. */
103
+ meta: Record<string, unknown> | null;
102
104
  }
103
105
  /** A transition rule between two states */
104
106
  export interface Transition {
@@ -195,6 +197,7 @@ export interface CreateStateInput {
195
197
  onEnter?: OnEnterConfig;
196
198
  onExit?: OnExitConfig;
197
199
  retryAfterMs?: number;
200
+ meta?: Record<string, unknown>;
198
201
  }
199
202
  /** Input for adding a transition rule */
200
203
  export interface CreateTransitionInput {
@@ -0,0 +1 @@
1
+ ALTER TABLE `state_definitions` ADD `meta` text;