@wopr-network/defcon 0.2.0 → 0.2.2

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 (102) hide show
  1. package/dist/src/execution/cli.js +0 -0
  2. package/package.json +3 -2
  3. package/dist/api/router.d.ts +0 -24
  4. package/dist/api/router.js +0 -44
  5. package/dist/api/server.d.ts +0 -13
  6. package/dist/api/server.js +0 -280
  7. package/dist/api/wire-types.d.ts +0 -46
  8. package/dist/api/wire-types.js +0 -5
  9. package/dist/config/db-path.d.ts +0 -1
  10. package/dist/config/db-path.js +0 -1
  11. package/dist/config/exporter.d.ts +0 -3
  12. package/dist/config/exporter.js +0 -87
  13. package/dist/config/index.d.ts +0 -4
  14. package/dist/config/index.js +0 -4
  15. package/dist/config/seed-loader.d.ts +0 -10
  16. package/dist/config/seed-loader.js +0 -108
  17. package/dist/config/zod-schemas.d.ts +0 -165
  18. package/dist/config/zod-schemas.js +0 -283
  19. package/dist/cors.d.ts +0 -8
  20. package/dist/cors.js +0 -21
  21. package/dist/engine/constants.d.ts +0 -1
  22. package/dist/engine/constants.js +0 -1
  23. package/dist/engine/engine.d.ts +0 -69
  24. package/dist/engine/engine.js +0 -485
  25. package/dist/engine/event-emitter.d.ts +0 -9
  26. package/dist/engine/event-emitter.js +0 -19
  27. package/dist/engine/event-types.d.ts +0 -105
  28. package/dist/engine/event-types.js +0 -1
  29. package/dist/engine/flow-spawner.d.ts +0 -8
  30. package/dist/engine/flow-spawner.js +0 -28
  31. package/dist/engine/gate-command-validator.d.ts +0 -6
  32. package/dist/engine/gate-command-validator.js +0 -46
  33. package/dist/engine/gate-evaluator.d.ts +0 -12
  34. package/dist/engine/gate-evaluator.js +0 -233
  35. package/dist/engine/handlebars.d.ts +0 -9
  36. package/dist/engine/handlebars.js +0 -51
  37. package/dist/engine/index.d.ts +0 -12
  38. package/dist/engine/index.js +0 -7
  39. package/dist/engine/invocation-builder.d.ts +0 -18
  40. package/dist/engine/invocation-builder.js +0 -58
  41. package/dist/engine/on-enter.d.ts +0 -8
  42. package/dist/engine/on-enter.js +0 -102
  43. package/dist/engine/ssrf-guard.d.ts +0 -22
  44. package/dist/engine/ssrf-guard.js +0 -159
  45. package/dist/engine/state-machine.d.ts +0 -12
  46. package/dist/engine/state-machine.js +0 -74
  47. package/dist/execution/active-runner.d.ts +0 -45
  48. package/dist/execution/active-runner.js +0 -165
  49. package/dist/execution/admin-schemas.d.ts +0 -116
  50. package/dist/execution/admin-schemas.js +0 -125
  51. package/dist/execution/cli.d.ts +0 -57
  52. package/dist/execution/cli.js +0 -498
  53. package/dist/execution/handlers/admin.d.ts +0 -67
  54. package/dist/execution/handlers/admin.js +0 -200
  55. package/dist/execution/handlers/flow.d.ts +0 -25
  56. package/dist/execution/handlers/flow.js +0 -289
  57. package/dist/execution/handlers/query.d.ts +0 -31
  58. package/dist/execution/handlers/query.js +0 -64
  59. package/dist/execution/index.d.ts +0 -4
  60. package/dist/execution/index.js +0 -3
  61. package/dist/execution/mcp-helpers.d.ts +0 -42
  62. package/dist/execution/mcp-helpers.js +0 -23
  63. package/dist/execution/mcp-server.d.ts +0 -33
  64. package/dist/execution/mcp-server.js +0 -1020
  65. package/dist/execution/provision-worktree.d.ts +0 -16
  66. package/dist/execution/provision-worktree.js +0 -123
  67. package/dist/execution/tool-schemas.d.ts +0 -40
  68. package/dist/execution/tool-schemas.js +0 -44
  69. package/dist/logger.d.ts +0 -8
  70. package/dist/logger.js +0 -12
  71. package/dist/main.d.ts +0 -14
  72. package/dist/main.js +0 -28
  73. package/dist/repositories/drizzle/entity.repo.d.ts +0 -27
  74. package/dist/repositories/drizzle/entity.repo.js +0 -190
  75. package/dist/repositories/drizzle/event.repo.d.ts +0 -12
  76. package/dist/repositories/drizzle/event.repo.js +0 -24
  77. package/dist/repositories/drizzle/flow.repo.d.ts +0 -22
  78. package/dist/repositories/drizzle/flow.repo.js +0 -364
  79. package/dist/repositories/drizzle/gate.repo.d.ts +0 -16
  80. package/dist/repositories/drizzle/gate.repo.js +0 -98
  81. package/dist/repositories/drizzle/index.d.ts +0 -6
  82. package/dist/repositories/drizzle/index.js +0 -7
  83. package/dist/repositories/drizzle/invocation.repo.d.ts +0 -23
  84. package/dist/repositories/drizzle/invocation.repo.js +0 -199
  85. package/dist/repositories/drizzle/schema.d.ts +0 -1932
  86. package/dist/repositories/drizzle/schema.js +0 -155
  87. package/dist/repositories/drizzle/transition-log.repo.d.ts +0 -11
  88. package/dist/repositories/drizzle/transition-log.repo.js +0 -42
  89. package/dist/repositories/interfaces.d.ts +0 -321
  90. package/dist/repositories/interfaces.js +0 -2
  91. package/dist/utils/redact.d.ts +0 -2
  92. package/dist/utils/redact.js +0 -62
  93. package/gates/blocking-graph.d.ts +0 -26
  94. package/gates/blocking-graph.js +0 -102
  95. package/gates/test/bad-return-gate.d.ts +0 -1
  96. package/gates/test/bad-return-gate.js +0 -4
  97. package/gates/test/passing-gate.d.ts +0 -2
  98. package/gates/test/passing-gate.js +0 -3
  99. package/gates/test/slow-gate.d.ts +0 -2
  100. package/gates/test/slow-gate.js +0 -5
  101. package/gates/test/throwing-gate.d.ts +0 -1
  102. package/gates/test/throwing-gate.js +0 -3
@@ -1,155 +0,0 @@
1
- import { index, integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
2
- // ─── Definition Tables ───
3
- export const flowDefinitions = sqliteTable("flow_definitions", {
4
- id: text("id").primaryKey(),
5
- name: text("name").notNull().unique(),
6
- description: text("description"),
7
- entitySchema: text("entity_schema", { mode: "json" }),
8
- initialState: text("initial_state").notNull(),
9
- maxConcurrent: integer("max_concurrent").default(0),
10
- maxConcurrentPerRepo: integer("max_concurrent_per_repo").default(0),
11
- affinityWindowMs: integer("affinity_window_ms").default(300000),
12
- gateTimeoutMs: integer("gate_timeout_ms"),
13
- version: integer("version").default(1),
14
- createdBy: text("created_by"),
15
- discipline: text("discipline"),
16
- defaultModelTier: text("default_model_tier"),
17
- timeoutPrompt: text("timeout_prompt"),
18
- createdAt: integer("created_at"),
19
- updatedAt: integer("updated_at"),
20
- });
21
- export const stateDefinitions = sqliteTable("state_definitions", {
22
- id: text("id").primaryKey(),
23
- flowId: text("flow_id")
24
- .notNull()
25
- .references(() => flowDefinitions.id),
26
- name: text("name").notNull(),
27
- agentRole: text("agent_role"),
28
- modelTier: text("model_tier"),
29
- mode: text("mode").default("passive"),
30
- promptTemplate: text("prompt_template"),
31
- constraints: text("constraints", { mode: "json" }),
32
- onEnter: text("on_enter", { mode: "json" }),
33
- }, (table) => ({
34
- flowNameUnique: uniqueIndex("state_definitions_flow_name_unique").on(table.flowId, table.name),
35
- }));
36
- export const gateDefinitions = sqliteTable("gate_definitions", {
37
- id: text("id").primaryKey(),
38
- name: text("name").notNull().unique(),
39
- type: text("type").notNull(),
40
- command: text("command"),
41
- functionRef: text("function_ref"),
42
- apiConfig: text("api_config", { mode: "json" }),
43
- timeoutMs: integer("timeout_ms"),
44
- failurePrompt: text("failure_prompt"),
45
- timeoutPrompt: text("timeout_prompt"),
46
- });
47
- export const transitionRules = sqliteTable("transition_rules", {
48
- id: text("id").primaryKey(),
49
- flowId: text("flow_id")
50
- .notNull()
51
- .references(() => flowDefinitions.id),
52
- fromState: text("from_state").notNull(),
53
- toState: text("to_state").notNull(),
54
- trigger: text("trigger").notNull(),
55
- gateId: text("gate_id").references(() => gateDefinitions.id),
56
- condition: text("condition"),
57
- priority: integer("priority").default(0),
58
- spawnFlow: text("spawn_flow"),
59
- spawnTemplate: text("spawn_template"),
60
- createdAt: integer("created_at"),
61
- });
62
- export const flowVersions = sqliteTable("flow_versions", {
63
- id: text("id").primaryKey(),
64
- flowId: text("flow_id")
65
- .notNull()
66
- .references(() => flowDefinitions.id),
67
- version: integer("version").notNull(),
68
- snapshot: text("snapshot", { mode: "json" }),
69
- changedBy: text("changed_by"),
70
- changeReason: text("change_reason"),
71
- createdAt: integer("created_at"),
72
- }, (table) => ({
73
- flowVersionUnique: uniqueIndex("flow_versions_flow_version_unique").on(table.flowId, table.version),
74
- }));
75
- // ─── Runtime Tables ───
76
- export const entities = sqliteTable("entities", {
77
- id: text("id").primaryKey(),
78
- flowId: text("flow_id")
79
- .notNull()
80
- .references(() => flowDefinitions.id),
81
- state: text("state").notNull(),
82
- refs: text("refs", { mode: "json" }),
83
- artifacts: text("artifacts", { mode: "json" }),
84
- claimedBy: text("claimed_by"),
85
- claimedAt: integer("claimed_at"),
86
- flowVersion: integer("flow_version"),
87
- priority: integer("priority").default(0),
88
- createdAt: integer("created_at"),
89
- updatedAt: integer("updated_at"),
90
- affinityWorkerId: text("affinity_worker_id"),
91
- affinityRole: text("affinity_role"),
92
- affinityExpiresAt: integer("affinity_expires_at"),
93
- }, (table) => ({
94
- flowStateIdx: index("entities_flow_state_idx").on(table.flowId, table.state),
95
- claimIdx: index("entities_claim_idx").on(table.flowId, table.state, table.claimedBy),
96
- affinityIdx: index("entities_affinity_idx").on(table.affinityWorkerId, table.affinityRole, table.affinityExpiresAt),
97
- }));
98
- export const invocations = sqliteTable("invocations", {
99
- id: text("id").primaryKey(),
100
- entityId: text("entity_id")
101
- .notNull()
102
- .references(() => entities.id),
103
- stage: text("stage").notNull(),
104
- agentRole: text("agent_role"),
105
- mode: text("mode").notNull(),
106
- prompt: text("prompt").notNull(),
107
- context: text("context", { mode: "json" }),
108
- claimedBy: text("claimed_by"),
109
- claimedAt: integer("claimed_at"),
110
- startedAt: integer("started_at"),
111
- completedAt: integer("completed_at"),
112
- failedAt: integer("failed_at"),
113
- signal: text("signal"),
114
- artifacts: text("artifacts", { mode: "json" }),
115
- error: text("error"),
116
- ttlMs: integer("ttl_ms").default(1800000),
117
- createdAt: integer("created_at"),
118
- }, (table) => ({
119
- entityIdx: index("invocations_entity_idx").on(table.entityId),
120
- }));
121
- export const gateResults = sqliteTable("gate_results", {
122
- id: text("id").primaryKey(),
123
- entityId: text("entity_id")
124
- .notNull()
125
- .references(() => entities.id),
126
- gateId: text("gate_id")
127
- .notNull()
128
- .references(() => gateDefinitions.id),
129
- passed: integer("passed").notNull(),
130
- output: text("output"),
131
- evaluatedAt: integer("evaluated_at"),
132
- });
133
- export const entityHistory = sqliteTable("entity_history", {
134
- id: text("id").primaryKey(),
135
- entityId: text("entity_id")
136
- .notNull()
137
- .references(() => entities.id),
138
- fromState: text("from_state"),
139
- toState: text("to_state").notNull(),
140
- trigger: text("trigger"),
141
- invocationId: text("invocation_id").references(() => invocations.id),
142
- timestamp: integer("timestamp").notNull(),
143
- }, (table) => ({
144
- entityTimestampIdx: index("entity_history_entity_ts_idx").on(table.entityId, table.timestamp),
145
- }));
146
- export const events = sqliteTable("events", {
147
- id: text("id").primaryKey(),
148
- type: text("type").notNull(),
149
- entityId: text("entity_id"),
150
- flowId: text("flow_id"),
151
- payload: text("payload", { mode: "json" }),
152
- emittedAt: integer("emitted_at").notNull(),
153
- }, (table) => ({
154
- typeEmittedIdx: index("events_type_emitted_idx").on(table.type, table.emittedAt),
155
- }));
@@ -1,11 +0,0 @@
1
- import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
2
- import type { ITransitionLogRepository, TransitionLog } from "../interfaces.js";
3
- import type * as schema from "./schema.js";
4
- type Db = BetterSQLite3Database<typeof schema>;
5
- export declare class DrizzleTransitionLogRepository implements ITransitionLogRepository {
6
- private db;
7
- constructor(db: Db);
8
- record(log: Omit<TransitionLog, "id">): Promise<TransitionLog>;
9
- historyFor(entityId: string): Promise<TransitionLog[]>;
10
- }
11
- export {};
@@ -1,42 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { asc, eq, sql } from "drizzle-orm";
3
- import { entityHistory } from "./schema.js";
4
- export class DrizzleTransitionLogRepository {
5
- db;
6
- constructor(db) {
7
- this.db = db;
8
- }
9
- async record(log) {
10
- const id = randomUUID();
11
- this.db
12
- .insert(entityHistory)
13
- .values({
14
- id,
15
- entityId: log.entityId,
16
- fromState: log.fromState ?? null,
17
- toState: log.toState,
18
- trigger: log.trigger ?? null,
19
- invocationId: log.invocationId ?? null,
20
- timestamp: log.timestamp.getTime(),
21
- })
22
- .run();
23
- return { id, ...log };
24
- }
25
- async historyFor(entityId) {
26
- const rows = this.db
27
- .select()
28
- .from(entityHistory)
29
- .where(eq(entityHistory.entityId, entityId))
30
- .orderBy(asc(entityHistory.timestamp), sql `rowid`)
31
- .all();
32
- return rows.map((r) => ({
33
- id: r.id,
34
- entityId: r.entityId,
35
- fromState: r.fromState,
36
- toState: r.toState,
37
- trigger: r.trigger,
38
- invocationId: r.invocationId,
39
- timestamp: new Date(r.timestamp),
40
- }));
41
- }
42
- }
@@ -1,321 +0,0 @@
1
- /** External-system reference map keyed by adapter name */
2
- export type Refs = Record<string, {
3
- adapter: string;
4
- id: string;
5
- [key: string]: unknown;
6
- }>;
7
- /** Freeform key-value artifact bag */
8
- export type Artifacts = Record<string, unknown>;
9
- /** Configuration for running a command when an entity enters a state */
10
- export interface OnEnterConfig {
11
- command: string;
12
- artifacts: string[];
13
- timeout_ms?: number;
14
- }
15
- /** Invocation execution mode */
16
- export type Mode = "active" | "passive";
17
- /** Runtime entity tracked through a flow */
18
- export interface Entity {
19
- id: string;
20
- flowId: string;
21
- state: string;
22
- refs: Refs | null;
23
- artifacts: Artifacts | null;
24
- claimedBy: string | null;
25
- claimedAt: Date | null;
26
- flowVersion: number;
27
- priority: number;
28
- createdAt: Date;
29
- updatedAt: Date;
30
- affinityWorkerId: string | null;
31
- affinityRole: string | null;
32
- affinityExpiresAt: Date | null;
33
- }
34
- /** A single agent invocation tied to an entity */
35
- export interface Invocation {
36
- id: string;
37
- entityId: string;
38
- stage: string;
39
- mode: Mode;
40
- prompt: string;
41
- context: Record<string, unknown> | null;
42
- claimedBy: string | null;
43
- claimedAt: Date | null;
44
- startedAt: Date | null;
45
- completedAt: Date | null;
46
- failedAt: Date | null;
47
- signal: string | null;
48
- artifacts: Artifacts | null;
49
- error: string | null;
50
- ttlMs: number;
51
- }
52
- /** Result of evaluating a gate against an entity */
53
- export interface GateResult {
54
- id: string;
55
- entityId: string;
56
- gateId: string;
57
- passed: boolean;
58
- output: string | null;
59
- evaluatedAt: Date | null;
60
- }
61
- /**
62
- * Entity with pre-fetched related data for use in Handlebars template helpers.
63
- * The `invocations` and `gateResults` fields are populated by the engine before
64
- * rendering prompt templates; they are never present on raw DB rows.
65
- */
66
- export interface EnrichedEntity extends Entity {
67
- invocations?: Invocation[];
68
- gateResults?: GateResult[];
69
- }
70
- /** Audit-log entry for an entity state transition */
71
- export interface TransitionLog {
72
- id: string;
73
- entityId: string;
74
- fromState: string | null;
75
- toState: string;
76
- trigger: string | null;
77
- invocationId: string | null;
78
- timestamp: Date;
79
- }
80
- /** A state within a flow definition */
81
- export interface State {
82
- id: string;
83
- flowId: string;
84
- name: string;
85
- modelTier: string | null;
86
- mode: Mode;
87
- promptTemplate: string | null;
88
- constraints: Record<string, unknown> | null;
89
- onEnter: OnEnterConfig | null;
90
- }
91
- /** A transition rule between two states */
92
- export interface Transition {
93
- id: string;
94
- flowId: string;
95
- fromState: string;
96
- toState: string;
97
- trigger: string;
98
- gateId: string | null;
99
- condition: string | null;
100
- priority: number;
101
- spawnFlow: string | null;
102
- spawnTemplate: string | null;
103
- createdAt: Date | null;
104
- }
105
- /** A gate definition (quality check) */
106
- export interface Gate {
107
- id: string;
108
- name: string;
109
- type: string;
110
- command: string | null;
111
- functionRef: string | null;
112
- apiConfig: Record<string, unknown> | null;
113
- timeoutMs: number | null;
114
- failurePrompt: string | null;
115
- timeoutPrompt: string | null;
116
- }
117
- /** A complete flow definition with its states and transitions */
118
- export interface Flow {
119
- id: string;
120
- name: string;
121
- description: string | null;
122
- entitySchema: Record<string, unknown> | null;
123
- initialState: string;
124
- maxConcurrent: number;
125
- maxConcurrentPerRepo: number;
126
- affinityWindowMs: number;
127
- gateTimeoutMs: number | null;
128
- version: number;
129
- createdBy: string | null;
130
- discipline: string | null;
131
- defaultModelTier: string | null;
132
- timeoutPrompt: string | null;
133
- createdAt: Date | null;
134
- updatedAt: Date | null;
135
- states: State[];
136
- transitions: Transition[];
137
- }
138
- /** A versioned snapshot of a flow */
139
- export interface FlowVersion {
140
- id: string;
141
- flowId: string;
142
- version: number;
143
- snapshot: Record<string, unknown>;
144
- changedBy: string | null;
145
- changeReason: string | null;
146
- createdAt: Date | null;
147
- }
148
- /** Input for creating a new flow */
149
- export interface CreateFlowInput {
150
- name: string;
151
- description?: string;
152
- entitySchema?: Record<string, unknown>;
153
- initialState: string;
154
- maxConcurrent?: number;
155
- maxConcurrentPerRepo?: number;
156
- affinityWindowMs?: number;
157
- gateTimeoutMs?: number;
158
- createdBy?: string;
159
- discipline?: string;
160
- defaultModelTier?: string;
161
- timeoutPrompt?: string;
162
- }
163
- /** Input for adding a state to a flow */
164
- export interface CreateStateInput {
165
- name: string;
166
- modelTier?: string;
167
- mode?: Mode;
168
- promptTemplate?: string;
169
- constraints?: Record<string, unknown>;
170
- onEnter?: OnEnterConfig;
171
- }
172
- /** Input for adding a transition rule */
173
- export interface CreateTransitionInput {
174
- fromState: string;
175
- toState: string;
176
- trigger: string;
177
- gateId?: string;
178
- condition?: string;
179
- priority?: number;
180
- spawnFlow?: string;
181
- spawnTemplate?: string;
182
- }
183
- /** Input for creating a gate definition */
184
- export interface CreateGateInput {
185
- name: string;
186
- type: string;
187
- command?: string;
188
- functionRef?: string;
189
- apiConfig?: Record<string, unknown>;
190
- timeoutMs?: number;
191
- failurePrompt?: string;
192
- timeoutPrompt?: string;
193
- }
194
- /** Data-access contract for entity lifecycle operations. */
195
- export interface IEntityRepository {
196
- /** Create a new entity in the given flow's initial state. */
197
- create(flowId: string, initialState: string, refs?: Refs): Promise<Entity>;
198
- /** Get an entity by ID, or null if not found. */
199
- get(id: string): Promise<Entity | null>;
200
- /** Find entities in a given flow and state, up to an optional limit. */
201
- findByFlowAndState(flowId: string, state: string, limit?: number): Promise<Entity[]>;
202
- /** Return true if at least one entity exists in the given flow across any of the given states. */
203
- hasAnyInFlowAndState(flowId: string, stateNames: string[]): Promise<boolean>;
204
- /** Transition an entity to a new state, recording the trigger and optional artifacts. */
205
- transition(id: string, toState: string, trigger: string, artifacts?: Partial<Artifacts>): Promise<Entity>;
206
- /** Merge partial artifacts into an entity's existing artifact bag. Performs a shallow merge
207
- * ({ ...existing, ...artifacts }) — only the specified keys are updated; unspecified keys are preserved. */
208
- updateArtifacts(id: string, artifacts: Partial<Artifacts>): Promise<void>;
209
- /** Atomically claim one unclaimed entity in the given flow+state for the specified agent. Returns null if none available. Uses compare-and-swap (UPDATE WHERE claimedBy IS NULL). */
210
- claim(flowId: string, state: string, agentId: string): Promise<Entity | null>;
211
- /** Atomically claim a specific entity by ID for the specified agent. Returns null if already claimed. Uses compare-and-swap (UPDATE WHERE claimedBy IS NULL). */
212
- claimById(entityId: string, agentId: string): Promise<Entity | null>;
213
- /** Release a claimed entity, clearing claimedBy and claimedAt. */
214
- release(entityId: string, agentId: string): Promise<void>;
215
- /** Find entities whose claim has expired beyond ttlMs and release them. Returns the IDs of released entities. */
216
- reapExpired(ttlMs: number): Promise<string[]>;
217
- /** Set affinity metadata on an entity, recording the last worker that touched it. */
218
- setAffinity(entityId: string, workerId: string, role: string, expiresAt: Date): Promise<void>;
219
- /** Clear expired affinity records. Returns the IDs of entities whose affinity was cleared. */
220
- clearExpiredAffinity(): Promise<string[]>;
221
- /** Atomically append a spawned child entry to the parent entity's artifacts.spawnedChildren array.
222
- * Reads the current array and writes back in a single transaction to prevent TOCTOU races. */
223
- appendSpawnedChild(parentId: string, entry: {
224
- childId: string;
225
- childFlow: string;
226
- spawnedAt: string;
227
- }): Promise<void>;
228
- }
229
- /** Fields that can be updated on a flow's top-level definition */
230
- export type UpdateFlowInput = Partial<Omit<Flow, "id" | "states" | "transitions" | "createdAt" | "updatedAt">>;
231
- /** Fields that can be updated on a state definition */
232
- export type UpdateStateInput = Partial<Omit<State, "id" | "flowId">>;
233
- /** Fields that can be updated on a transition rule */
234
- export type UpdateTransitionInput = Partial<Omit<Transition, "id" | "flowId">>;
235
- /** Data-access contract for flow definition CRUD and versioning. */
236
- export interface IFlowRepository {
237
- /** Create a new flow definition. */
238
- create(flow: CreateFlowInput): Promise<Flow>;
239
- /** List all flow definitions. */
240
- list(): Promise<Flow[]>;
241
- /** Get a flow by ID, including its states and transitions. Returns null if not found. */
242
- get(id: string): Promise<Flow | null>;
243
- /** Get a flow by unique name. Returns null if not found. */
244
- getByName(name: string): Promise<Flow | null>;
245
- /** Update a flow's top-level fields. */
246
- update(id: string, changes: UpdateFlowInput): Promise<Flow>;
247
- /** Add a state definition to a flow. */
248
- addState(flowId: string, state: CreateStateInput): Promise<State>;
249
- /** Update an existing state definition. */
250
- updateState(stateId: string, changes: UpdateStateInput): Promise<State>;
251
- /** Add a transition rule to a flow. */
252
- addTransition(flowId: string, transition: CreateTransitionInput): Promise<Transition>;
253
- /** Update an existing transition rule. */
254
- updateTransition(transitionId: string, changes: UpdateTransitionInput): Promise<Transition>;
255
- /** Create a versioned snapshot of the current flow definition. */
256
- snapshot(flowId: string): Promise<FlowVersion>;
257
- /** Restore a flow definition to a previous version. */
258
- restore(flowId: string, version: number): Promise<void>;
259
- /** List all flow definitions. */
260
- listAll(): Promise<Flow[]>;
261
- }
262
- /** Data-access contract for invocation lifecycle and claiming. */
263
- export interface IInvocationRepository {
264
- /** Create a new invocation for an entity at a given stage. */
265
- create(entityId: string, stage: string, prompt: string, mode: Mode, ttlMs?: number, context?: Record<string, unknown>): Promise<Invocation>;
266
- /** Get an invocation by ID, or null if not found. */
267
- get(id: string): Promise<Invocation | null>;
268
- /** Atomically claim an unclaimed invocation for the specified agent. Uses compare-and-swap (UPDATE WHERE claimedBy IS NULL). Returns null if already claimed. */
269
- claim(invocationId: string, agentId: string): Promise<Invocation | null>;
270
- /** Mark an invocation as completed with a signal and optional artifacts. */
271
- complete(id: string, signal: string, artifacts?: Artifacts): Promise<Invocation>;
272
- /** Mark an invocation as failed with an error message. */
273
- fail(id: string, error: string): Promise<Invocation>;
274
- /** Release a claim on an invocation, making it available for another worker to claim. */
275
- releaseClaim(id: string): Promise<void>;
276
- /** Find all invocations for a given entity. */
277
- findByEntity(entityId: string): Promise<Invocation[]>;
278
- /** Find unclaimed invocations where the entity has unexpired affinity for the given worker and role. */
279
- findUnclaimedWithAffinity(flowId: string, role: string, workerId: string): Promise<Invocation[]>;
280
- /** Find all unclaimed invocations in a flow, regardless of agentRole. For discipline-based claiming. */
281
- findUnclaimedByFlow(flowId: string): Promise<Invocation[]>;
282
- /** Find all invocations for a given flow (across all entities). */
283
- findByFlow(flowId: string): Promise<Invocation[]>;
284
- /** Find and mark expired invocations (where now - claimedAt > row's ttlMs). Returns the expired invocations. */
285
- reapExpired(): Promise<Invocation[]>;
286
- /** Find unclaimed active-mode invocations, optionally filtered by flow. */
287
- findUnclaimedActive(flowId?: string): Promise<Invocation[]>;
288
- /** Count active invocations (claimed, not completed/failed) for a flow. */
289
- countActiveByFlow(flowId: string): Promise<number>;
290
- /** Count pending invocations (unclaimed, not completed/failed) for a flow. */
291
- countPendingByFlow(flowId: string): Promise<number>;
292
- }
293
- /** Data-access contract for entity state-transition audit trails. */
294
- export interface ITransitionLogRepository {
295
- /** Record a state transition for an entity. */
296
- record(log: Omit<TransitionLog, "id">): Promise<TransitionLog>;
297
- /** Get full transition history for an entity, ordered by timestamp. */
298
- historyFor(entityId: string): Promise<TransitionLog[]>;
299
- }
300
- /** Data-access contract for emitting definition-change events. */
301
- export interface IEventRepository {
302
- /** Emit a definition change event for a tool action. */
303
- emitDefinitionChanged(flowId: string | null, tool: string, payload: Record<string, unknown>): Promise<void>;
304
- }
305
- /** Data-access contract for gate definitions and result recording. */
306
- export interface IGateRepository {
307
- /** Create a new gate definition. */
308
- create(gate: CreateGateInput): Promise<Gate>;
309
- /** Get a gate by ID, or null if not found. */
310
- get(id: string): Promise<Gate | null>;
311
- /** Get a gate by unique name, or null if not found. */
312
- getByName(name: string): Promise<Gate | null>;
313
- /** List all gate definitions. */
314
- listAll(): Promise<Gate[]>;
315
- /** Record the result of evaluating a gate against an entity. */
316
- record(entityId: string, gateId: string, passed: boolean, output: string): Promise<GateResult>;
317
- /** Get all gate results for a given entity. */
318
- resultsFor(entityId: string): Promise<GateResult[]>;
319
- /** Update mutable fields on a gate definition. */
320
- update(id: string, changes: Partial<Pick<Gate, "command" | "functionRef" | "apiConfig" | "timeoutMs" | "failurePrompt" | "timeoutPrompt">>): Promise<Gate>;
321
- }
@@ -1,2 +0,0 @@
1
- // Repository interfaces — I*Repository contracts
2
- export {};
@@ -1,2 +0,0 @@
1
- export declare function redactString(value: string, maxLength?: number): string;
2
- export declare function redact(value: unknown): unknown;
@@ -1,62 +0,0 @@
1
- // Sensitive term must appear as a whole word component in camelCase/snake_case keys.
2
- // Split on underscores and camelCase boundaries before matching, so e.g. "authHeader"
3
- // is redacted (contains "auth" as a component) but "authorId" is not ("author" ≠ "auth").
4
- const SENSITIVE_TERMS = new Set(["token", "key", "secret", "password", "bearer", "auth", "credential"]);
5
- function isSensitiveKey(k) {
6
- // Split on: underscores, spaces, or camelCase transitions (lower→upper, upper→upper+lower)
7
- const words = k.split(/[_\s]+|(?<=[a-z\d])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/);
8
- return words.some((w) => SENSITIVE_TERMS.has(w.toLowerCase()));
9
- }
10
- const DESCRIPTION_MAX = 100;
11
- // Patterns to strip from string content
12
- const CREDENTIAL_PATTERNS = [
13
- /Bearer\s+\S+/gi,
14
- /\bsk-ant-\S+/gi,
15
- /\bsk-\S{20,}/gi,
16
- /(?:password|secret|token|key|auth)[\s]*[=:]\s*\S+/gi,
17
- ];
18
- export function redactString(value, maxLength = 500) {
19
- let result = value;
20
- for (const pattern of CREDENTIAL_PATTERNS) {
21
- result = result.replace(pattern, "[REDACTED]");
22
- }
23
- if (result.length > maxLength) {
24
- result = `${result.slice(0, maxLength)}...`;
25
- }
26
- return result;
27
- }
28
- export function redact(value) {
29
- return walk(value, new WeakSet());
30
- }
31
- function walk(value, seen) {
32
- if (value === null || value === undefined)
33
- return value;
34
- if (typeof value !== "object")
35
- return value;
36
- if (value instanceof Date)
37
- return value;
38
- if (value instanceof Error) {
39
- return walk({ name: value.name, message: value.message, stack: value.stack }, seen);
40
- }
41
- const obj = value;
42
- if (seen.has(obj))
43
- return "[Circular]";
44
- seen.add(obj);
45
- if (Array.isArray(obj)) {
46
- return obj.map((item) => walk(item, seen));
47
- }
48
- const result = {};
49
- for (const k of Object.keys(obj)) {
50
- const v = obj[k];
51
- if (isSensitiveKey(k)) {
52
- result[k] = "[REDACTED]";
53
- }
54
- else if (k === "description" && typeof v === "string" && v.length > DESCRIPTION_MAX) {
55
- result[k] = `${v.slice(0, DESCRIPTION_MAX)}...`;
56
- }
57
- else {
58
- result[k] = walk(v, seen);
59
- }
60
- }
61
- return result;
62
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * Custom gate: checks whether all Linear blockers for an entity's issue
3
- * have corresponding merged PRs on GitHub.
4
- *
5
- * Referenced by seed as: "gates/blocking-graph.ts:isUnblocked"
6
- *
7
- * NOTE: Function gates are not yet evaluated by the engine (gate-evaluator.ts
8
- * throws for type "function"). This file exists as the implementation target
9
- * for when function gate support lands.
10
- *
11
- * NOTE: This file is intentionally outside src/ — it is loaded dynamically at
12
- * runtime, not compiled by the main build.
13
- */
14
- import type { Entity } from "../src/repositories/interfaces.js";
15
- export interface BlockingGraphResult {
16
- passed: boolean;
17
- output: string;
18
- }
19
- /**
20
- * Check if all blocking issues for the given entity have merged PRs.
21
- *
22
- * Expects entity.refs.linear.id to be the Linear issue ID.
23
- * Uses LINEAR_API_KEY env var for authentication.
24
- * Uses `gh` CLI to check PR merge status on GitHub.
25
- */
26
- export declare function isUnblocked(entity: Entity): Promise<BlockingGraphResult>;