@superatomai/sdk-node 0.0.52-dsp → 0.0.53-dsp

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -46,21 +46,36 @@ __export(index_exports, {
46
46
  UILogCollector: () => UILogCollector,
47
47
  UserManager: () => UserManager,
48
48
  anthropicLLM: () => anthropicLLM,
49
+ assertReadOnlySql: () => assertReadOnlySql,
50
+ createCodexSession: () => createCodexSession,
51
+ createCodingAgentSession: () => createCodingAgentSession,
49
52
  dashboardConversationHistory: () => dashboardConversationHistory,
53
+ describeTriggers: () => describeTriggers,
54
+ emitAgentEvent: () => emitAgentEvent,
50
55
  geminiLLM: () => geminiLLM,
56
+ getCodingAgent: () => getCodingAgent,
57
+ getCodingAgentsConfig: () => getCodingAgentsConfig,
51
58
  groqLLM: () => groqLLM,
52
59
  hybridRerank: () => hybridRerank,
60
+ listCodingAgents: () => listCodingAgents,
53
61
  llmUsageLogger: () => llmUsageLogger,
54
62
  logger: () => logger,
55
63
  normalizeScriptBody: () => normalizeScriptBody,
64
+ onAgentRun: () => onAgentRun,
56
65
  openaiLLM: () => openaiLLM,
57
66
  queryCache: () => queryCache,
67
+ registerCodingAgentCollection: () => registerCodingAgentCollection,
68
+ registerCodingAgents: () => registerCodingAgents,
58
69
  registerDashboardAgentCollection: () => registerDashboardAgentCollection,
59
70
  rerankChromaResults: () => rerankChromaResults,
60
71
  rerankConversationResults: () => rerankConversationResults,
61
72
  resolveScriptRecipeStore: () => resolveScriptRecipeStore,
73
+ runCodingAgent: () => runCodingAgent,
62
74
  runScript: () => runScript,
63
- userPromptErrorLogger: () => userPromptErrorLogger
75
+ startAgentTriggers: () => startAgentTriggers,
76
+ stopAgentTriggers: () => stopAgentTriggers,
77
+ userPromptErrorLogger: () => userPromptErrorLogger,
78
+ writeBridgeShims: () => writeBridgeShims
64
79
  });
65
80
  module.exports = __toCommonJS(index_exports);
66
81
 
@@ -841,6 +856,37 @@ var DashboardAgentAbortRequestMessageSchema = import_zod3.z.object({
841
856
  type: import_zod3.z.literal("DASHBOARD_AGENT_ABORT_REQ"),
842
857
  payload: DashboardAgentAbortRequestPayloadSchema
843
858
  });
859
+ var CodingAgentRequestPayloadSchema = import_zod3.z.object({
860
+ prompt: import_zod3.z.string(),
861
+ /** The chat thread. One agent session per thread, and the abort key. */
862
+ threadId: import_zod3.z.string(),
863
+ projectId: import_zod3.z.string().optional(),
864
+ userId: import_zod3.z.string().optional(),
865
+ /**
866
+ * Which registered agent to run (see registerCodingAgents). Omit for the
867
+ * deployment's plain chat agent — the pre-registry behaviour.
868
+ */
869
+ agentId: import_zod3.z.string().optional(),
870
+ /** Which harness to run. Defaults to whatever the deployment registered. */
871
+ harness: import_zod3.z.string().optional(),
872
+ /** Continue a prior agent session — the sessionId a previous run returned. */
873
+ resumeId: import_zod3.z.string().optional()
874
+ });
875
+ var CodingAgentRequestMessageSchema = import_zod3.z.object({
876
+ id: import_zod3.z.string(),
877
+ from: MessageParticipantSchema,
878
+ type: import_zod3.z.literal("CODING_AGENT_REQ"),
879
+ payload: CodingAgentRequestPayloadSchema
880
+ });
881
+ var CodingAgentAbortRequestPayloadSchema = import_zod3.z.object({
882
+ threadId: import_zod3.z.string()
883
+ });
884
+ var CodingAgentAbortRequestMessageSchema = import_zod3.z.object({
885
+ id: import_zod3.z.string().optional(),
886
+ from: MessageParticipantSchema,
887
+ type: import_zod3.z.literal("CODING_AGENT_ABORT_REQ"),
888
+ payload: CodingAgentAbortRequestPayloadSchema
889
+ });
844
890
  var ReportCompRequestPayloadSchema = import_zod3.z.object({
845
891
  prompt: import_zod3.z.string(),
846
892
  userId: import_zod3.z.string().optional(),
@@ -9111,12 +9157,12 @@ answer.
9111
9157
 
9112
9158
  // src/userResponse/prompt-loader.ts
9113
9159
  var PromptLoader = class {
9114
- constructor(config) {
9160
+ constructor(config2) {
9115
9161
  this.promptCache = /* @__PURE__ */ new Map();
9116
9162
  this.databaseRulesCache = /* @__PURE__ */ new Map();
9117
9163
  this.isInitialized = false;
9118
9164
  this.databaseType = "postgresql";
9119
- this.promptsDir = config?.promptsDir || import_path3.default.join(process.cwd(), ".prompts");
9165
+ this.promptsDir = config2?.promptsDir || import_path3.default.join(process.cwd(), ".prompts");
9120
9166
  }
9121
9167
  /**
9122
9168
  * Load a prompt template from file system OR fallback to hardcoded prompts
@@ -10032,7 +10078,7 @@ function validateAndFixSqlQuery(query, dbType) {
10032
10078
 
10033
10079
  // src/userResponse/agents/source-agent.ts
10034
10080
  var SourceAgent = class {
10035
- constructor(tool, config, streamBuffer, options) {
10081
+ constructor(tool, config2, streamBuffer, options) {
10036
10082
  this.attempts = 0;
10037
10083
  /**
10038
10084
  * Reference to this dispatch's own entry in trace.schemaNodes.sourceAgentSchemas
@@ -10042,7 +10088,7 @@ var SourceAgent = class {
10042
10088
  */
10043
10089
  this.traceSchemaEntry = null;
10044
10090
  this.tool = tool;
10045
- this.config = config;
10091
+ this.config = config2;
10046
10092
  this.streamBuffer = streamBuffer;
10047
10093
  this.options = options || {};
10048
10094
  }
@@ -11642,7 +11688,7 @@ var WRITE_ANALYSIS_TOOL_DEF = {
11642
11688
  var MAX_SCRIPT_ATTEMPTS = 3;
11643
11689
  var MAX_ANALYSIS_ATTEMPTS = 3;
11644
11690
  var MainAgent = class {
11645
- constructor(externalTools, config, scriptStore, turnId, streamBuffer, workflows = [], forkMode = false, editContext, componentCatalog = [], displayPreference) {
11691
+ constructor(externalTools, config2, scriptStore, turnId, streamBuffer, workflows = [], forkMode = false, editContext, componentCatalog = [], displayPreference) {
11646
11692
  this.createdFromPrompt = "";
11647
11693
  this.scriptState = {
11648
11694
  recipeId: null,
@@ -11688,7 +11734,7 @@ var MainAgent = class {
11688
11734
  this.displayPreference = null;
11689
11735
  this.externalTools = externalTools;
11690
11736
  this.workflows = workflows;
11691
- this.config = config;
11737
+ this.config = config2;
11692
11738
  this.streamBuffer = streamBuffer || new StreamBuffer();
11693
11739
  this.scriptStore = scriptStore ?? null;
11694
11740
  this.turnId = turnId ?? null;
@@ -13357,7 +13403,7 @@ function validateExternalTool(externalTool, executedTools, providerName) {
13357
13403
  const executedTool = executedTools?.find((t) => t.id === toolId);
13358
13404
  return { valid: true, executedTool };
13359
13405
  }
13360
- function validateAndCleanQuery(query, config) {
13406
+ function validateAndCleanQuery(query, config2) {
13361
13407
  if (!query) {
13362
13408
  return { query: null, wasModified: false };
13363
13409
  }
@@ -13365,12 +13411,12 @@ function validateAndCleanQuery(query, config) {
13365
13411
  let cleanedQuery = query;
13366
13412
  const queryStr = typeof query === "string" ? query : query?.sql || "";
13367
13413
  if (queryStr.includes("OPENJSON") || queryStr.includes("JSON_VALUE")) {
13368
- logger.warn(`[${config.providerName}] Query contains OPENJSON/JSON_VALUE (invalid - cannot parse tool result), setting query to null`);
13414
+ logger.warn(`[${config2.providerName}] Query contains OPENJSON/JSON_VALUE (invalid - cannot parse tool result), setting query to null`);
13369
13415
  return { query: null, wasModified: true };
13370
13416
  }
13371
13417
  const { query: fixedQuery, fixed, fixes } = validateAndFixSqlQuery(queryStr);
13372
13418
  if (fixed) {
13373
- logger.warn(`[${config.providerName}] SQL fixes applied to component query: ${fixes.join("; ")}`);
13419
+ logger.warn(`[${config2.providerName}] SQL fixes applied to component query: ${fixes.join("; ")}`);
13374
13420
  wasModified = true;
13375
13421
  if (typeof cleanedQuery === "string") {
13376
13422
  cleanedQuery = fixedQuery;
@@ -13378,36 +13424,36 @@ function validateAndCleanQuery(query, config) {
13378
13424
  cleanedQuery = { ...cleanedQuery, sql: fixedQuery };
13379
13425
  }
13380
13426
  }
13381
- const maxLimit = config.maxLimit ?? MAX_AGENT_QUERY_LIMIT;
13427
+ const maxLimit = config2.maxLimit ?? MAX_AGENT_QUERY_LIMIT;
13382
13428
  if (typeof cleanedQuery === "string") {
13383
- const limitedQuery = ensureQueryLimit(cleanedQuery, config.defaultLimit, maxLimit);
13429
+ const limitedQuery = ensureQueryLimit(cleanedQuery, config2.defaultLimit, maxLimit);
13384
13430
  if (limitedQuery !== cleanedQuery) wasModified = true;
13385
13431
  cleanedQuery = limitedQuery;
13386
13432
  } else if (cleanedQuery?.sql) {
13387
- const limitedSql = ensureQueryLimit(cleanedQuery.sql, config.defaultLimit, maxLimit);
13433
+ const limitedSql = ensureQueryLimit(cleanedQuery.sql, config2.defaultLimit, maxLimit);
13388
13434
  if (limitedSql !== cleanedQuery.sql) wasModified = true;
13389
13435
  cleanedQuery = { ...cleanedQuery, sql: limitedSql };
13390
13436
  }
13391
13437
  return { query: cleanedQuery, wasModified };
13392
13438
  }
13393
- function processComponentProps(props, executedTools, config) {
13439
+ function processComponentProps(props, executedTools, config2) {
13394
13440
  let cleanedProps = { ...props };
13395
13441
  if (cleanedProps.externalTool) {
13396
13442
  const { valid } = validateExternalTool(
13397
13443
  cleanedProps.externalTool,
13398
13444
  executedTools,
13399
- config.providerName
13445
+ config2.providerName
13400
13446
  );
13401
13447
  if (!valid) {
13402
13448
  cleanedProps.externalTool = null;
13403
13449
  }
13404
13450
  }
13405
13451
  if (cleanedProps.query) {
13406
- const { query } = validateAndCleanQuery(cleanedProps.query, config);
13452
+ const { query } = validateAndCleanQuery(cleanedProps.query, config2);
13407
13453
  cleanedProps.query = query;
13408
13454
  }
13409
13455
  if (cleanedProps.query && cleanedProps.externalTool) {
13410
- logger.info(`[${config.providerName}] Both query and externalTool exist, keeping both - frontend will decide`);
13456
+ logger.info(`[${config2.providerName}] Both query and externalTool exist, keeping both - frontend will decide`);
13411
13457
  }
13412
13458
  return cleanedProps;
13413
13459
  }
@@ -13980,15 +14026,15 @@ ${carried.trim()}
13980
14026
  }
13981
14027
  /** slug of name, with a short id suffix when the bare slug is already taken. */
13982
14028
  async computeFileBase(name, id) {
13983
- const slug = this.toSlug(name);
14029
+ const slug2 = this.toSlug(name);
13984
14030
  if (this.store) {
13985
14031
  try {
13986
- const taken = await this.store.fileBaseTaken(slug, id, this.projectId);
13987
- if (taken) return `${slug}-${id.slice(-6)}`;
14032
+ const taken = await this.store.fileBaseTaken(slug2, id, this.projectId);
14033
+ if (taken) return `${slug2}-${id.slice(-6)}`;
13988
14034
  } catch {
13989
14035
  }
13990
14036
  }
13991
- return slug;
14037
+ return slug2;
13992
14038
  }
13993
14039
  toSlug(name) {
13994
14040
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unnamed-script";
@@ -15377,8 +15423,8 @@ var schema = new Schema();
15377
15423
 
15378
15424
  // src/userResponse/services/query-execution-service.ts
15379
15425
  var QueryExecutionService = class {
15380
- constructor(config) {
15381
- this.config = config;
15426
+ constructor(config2) {
15427
+ this.config = config2;
15382
15428
  }
15383
15429
  /**
15384
15430
  * Get the cache key for a query
@@ -15611,12 +15657,12 @@ Fixed SQL query:`;
15611
15657
 
15612
15658
  // src/userResponse/services/tool-executor-service.ts
15613
15659
  var ToolExecutorService = class {
15614
- constructor(config) {
15660
+ constructor(config2) {
15615
15661
  this.queryAttempts = /* @__PURE__ */ new Map();
15616
15662
  this.toolAttempts = /* @__PURE__ */ new Map();
15617
15663
  this.executedToolsList = [];
15618
15664
  this.maxAttemptsReached = false;
15619
- this.config = config;
15665
+ this.config = config2;
15620
15666
  }
15621
15667
  /**
15622
15668
  * Reset state for a new execution
@@ -15928,13 +15974,13 @@ ${errorMsg}
15928
15974
 
15929
15975
  // src/userResponse/base-llm.ts
15930
15976
  var BaseLLM = class {
15931
- constructor(config) {
15932
- this.model = config?.model || this.getDefaultModel();
15933
- this.fastModel = config?.fastModel || this.getDefaultFastModel();
15934
- this.defaultLimit = config?.defaultLimit || 10;
15935
- this.apiKey = config?.apiKey;
15936
- this.modelStrategy = config?.modelStrategy || "fast";
15937
- this.conversationSimilarityThreshold = config?.conversationSimilarityThreshold || DEFAULT_CONVERSATION_SIMILARITY_THRESHOLD;
15977
+ constructor(config2) {
15978
+ this.model = config2?.model || this.getDefaultModel();
15979
+ this.fastModel = config2?.fastModel || this.getDefaultFastModel();
15980
+ this.defaultLimit = config2?.defaultLimit || 10;
15981
+ this.apiKey = config2?.apiKey;
15982
+ this.modelStrategy = config2?.modelStrategy || "fast";
15983
+ this.conversationSimilarityThreshold = config2?.conversationSimilarityThreshold || DEFAULT_CONVERSATION_SIMILARITY_THRESHOLD;
15938
15984
  this.queryService = new QueryExecutionService({
15939
15985
  defaultLimit: this.defaultLimit,
15940
15986
  getModelForTask: (taskType) => this.getModelForTask(taskType),
@@ -17123,8 +17169,8 @@ ${executedToolsText}`);
17123
17169
  // src/userResponse/anthropic.ts
17124
17170
  import_dotenv.default.config({ quiet: true });
17125
17171
  var AnthropicLLM = class extends BaseLLM {
17126
- constructor(config) {
17127
- super(config);
17172
+ constructor(config2) {
17173
+ super(config2);
17128
17174
  }
17129
17175
  getDefaultModel() {
17130
17176
  return "anthropic/claude-sonnet-4-5-20250929";
@@ -17145,8 +17191,8 @@ var anthropicLLM = new AnthropicLLM();
17145
17191
  var import_dotenv2 = __toESM(require("dotenv"));
17146
17192
  import_dotenv2.default.config({ quiet: true });
17147
17193
  var GroqLLM = class extends BaseLLM {
17148
- constructor(config) {
17149
- super(config);
17194
+ constructor(config2) {
17195
+ super(config2);
17150
17196
  }
17151
17197
  getDefaultModel() {
17152
17198
  return "groq/openai/gpt-oss-120b";
@@ -17167,8 +17213,8 @@ var groqLLM = new GroqLLM();
17167
17213
  var import_dotenv3 = __toESM(require("dotenv"));
17168
17214
  import_dotenv3.default.config({ quiet: true });
17169
17215
  var GeminiLLM = class extends BaseLLM {
17170
- constructor(config) {
17171
- super(config);
17216
+ constructor(config2) {
17217
+ super(config2);
17172
17218
  }
17173
17219
  getDefaultModel() {
17174
17220
  return "gemini/gemini-2.5-flash";
@@ -17189,8 +17235,8 @@ var geminiLLM = new GeminiLLM();
17189
17235
  var import_dotenv4 = __toESM(require("dotenv"));
17190
17236
  import_dotenv4.default.config({ quiet: true });
17191
17237
  var OpenAILLM = class extends BaseLLM {
17192
- constructor(config) {
17193
- super(config);
17238
+ constructor(config2) {
17239
+ super(config2);
17194
17240
  }
17195
17241
  getDefaultModel() {
17196
17242
  return "openai/gpt-4.1";
@@ -23658,6 +23704,1031 @@ async function handleDashboardAgentRequest(data, collections, sendMessage) {
23658
23704
  }
23659
23705
  }
23660
23706
 
23707
+ // src/codingAgent/drivers/codex.ts
23708
+ var dynamicImport = new Function("specifier", "return import(specifier)");
23709
+ var codexModulePromise = null;
23710
+ function loadCodex() {
23711
+ if (!codexModulePromise) {
23712
+ codexModulePromise = dynamicImport("@openai/codex-sdk").catch((err) => {
23713
+ codexModulePromise = null;
23714
+ throw new Error(
23715
+ `@openai/codex-sdk is not installed, so the codex harness is unavailable. It is an optionalDependency of this SDK, so an install that could not fetch it (disk, architecture, network) will have skipped it silently \u2014 re-run the deployment's install, or 'pnpm add @openai/codex-sdk@0.149.1' to add it directly. (${err?.message ?? err})`
23716
+ );
23717
+ });
23718
+ }
23719
+ return codexModulePromise;
23720
+ }
23721
+ var ENV_ALLOWLIST = ["PATH", "HOME", "USER", "LOGNAME", "SHELL", "LANG", "LC_ALL", "TMPDIR", "TERM", "NODE_EXTRA_CA_CERTS"];
23722
+ var ENV_PREFIX_ALLOWLIST = ["CODEX_", "OPENAI_"];
23723
+ var DONE_POLL_MS = 250;
23724
+ var USAGE_GRACE_MS = 4e3;
23725
+ var BUFFER_CAP = 64e3;
23726
+ var EVENT_CAP = 600;
23727
+ function emptyUsage() {
23728
+ return { inputTokens: 0, outputTokens: 0, reasoningTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, toolCalls: 0 };
23729
+ }
23730
+ function addUsage(acc, u) {
23731
+ acc.inputTokens += u.input_tokens ?? 0;
23732
+ acc.outputTokens += u.output_tokens ?? 0;
23733
+ acc.reasoningTokens += u.reasoning_output_tokens ?? 0;
23734
+ acc.cacheReadTokens += u.cached_input_tokens ?? 0;
23735
+ acc.cacheWriteTokens += u.cache_write_input_tokens ?? 0;
23736
+ }
23737
+ function normEvent(ev, done) {
23738
+ const it = ev.item;
23739
+ if (!it) return null;
23740
+ const status = it.status;
23741
+ switch (it.type) {
23742
+ case "agent_message":
23743
+ return { kind: "message", id: it.id, text: it.text ?? "", done };
23744
+ case "reasoning":
23745
+ return { kind: "reasoning", id: it.id, text: it.text ?? "", done };
23746
+ case "command_execution":
23747
+ return { kind: "command", id: it.id, command: it.command ?? "", output: it.aggregated_output ?? "", status, done };
23748
+ case "mcp_tool_call":
23749
+ return { kind: "tool", id: it.id, tool: { server: it.server, name: it.tool ?? "tool", args: it.arguments }, status, done };
23750
+ case "web_search":
23751
+ return { kind: "tool", id: it.id, tool: { name: "web_search", args: { query: it.query } }, status, done };
23752
+ case "file_change":
23753
+ return { kind: "file", id: it.id, text: (it.changes ?? []).map((c) => `${c.kind} ${c.path}`).join(", "), status, done };
23754
+ case "todo_list":
23755
+ return { kind: "todo", id: it.id, text: (it.items ?? []).map((t) => `${t.completed ? "[x]" : "[ ]"} ${t.text}`).join("\n"), done };
23756
+ case "error":
23757
+ return { kind: "error", id: it.id, text: it.message ?? "", done };
23758
+ default:
23759
+ return null;
23760
+ }
23761
+ }
23762
+ function markerFor(event) {
23763
+ switch (event.kind) {
23764
+ case "command":
23765
+ return event.command ? `\u2192 ${event.command.replace(/\s+/g, " ").slice(0, 160)}` : null;
23766
+ case "tool": {
23767
+ const args = event.tool?.args;
23768
+ const detail = args && typeof args === "object" ? String(args.sql ?? args.query ?? args.command ?? "").replace(/\s+/g, " ").slice(0, 140) : "";
23769
+ return `\u2192 ${event.tool?.name ?? "tool"}${detail ? " " + detail : ""}`;
23770
+ }
23771
+ case "file":
23772
+ return event.text ? `\u270E ${event.text}` : null;
23773
+ default:
23774
+ return null;
23775
+ }
23776
+ }
23777
+ function buildEnv(opts) {
23778
+ if (opts.inheritEnv) return opts.env ? { ...process.env, ...opts.env } : void 0;
23779
+ const env = {};
23780
+ for (const [k, v] of Object.entries(process.env)) {
23781
+ if (v === void 0) continue;
23782
+ if (ENV_ALLOWLIST.includes(k) || ENV_PREFIX_ALLOWLIST.some((p) => k.startsWith(p))) env[k] = v;
23783
+ }
23784
+ return { ...env, ...opts.env ?? {} };
23785
+ }
23786
+ function createCodexSession(opts) {
23787
+ const model = opts.model ?? process.env.CODEX_AGENT_MODEL;
23788
+ let thread = null;
23789
+ let resumeId = opts.resumeId;
23790
+ let threadId = opts.resumeId;
23791
+ let resumeFailed = false;
23792
+ let running = false;
23793
+ let buf = "";
23794
+ const eventLog = [];
23795
+ const threadOptions = () => ({
23796
+ ...model ? { model } : {},
23797
+ workingDirectory: opts.cwd,
23798
+ sandboxMode: opts.sandboxMode ?? "workspace-write",
23799
+ // Headless: a prompt for approval would hang forever with nobody to answer it.
23800
+ approvalPolicy: opts.approvalPolicy ?? "never",
23801
+ networkAccessEnabled: opts.networkAccessEnabled ?? false,
23802
+ skipGitRepoCheck: true,
23803
+ ...opts.reasoningEffort ? { modelReasoningEffort: opts.reasoningEffort } : {}
23804
+ });
23805
+ async function ensureThread() {
23806
+ if (thread) return thread;
23807
+ const { Codex } = await loadCodex();
23808
+ const codex = new Codex({
23809
+ ...opts.apiKey ? { apiKey: opts.apiKey } : {},
23810
+ ...opts.baseUrl ? { baseUrl: opts.baseUrl } : {},
23811
+ env: buildEnv(opts),
23812
+ // Tools reach codex as MCP servers; each server's name shows up on
23813
+ // every tool event, so the runner can tell ours from codex's own.
23814
+ // Only send a config override when there is something in it. An empty
23815
+ // object is truthy, so the naive check sent config:{mcp_servers:{}} on
23816
+ // every run — a pointless CLI override on a path that is otherwise
23817
+ // completely default.
23818
+ ...opts.mcpServers && Object.keys(opts.mcpServers).length || opts.extraConfig ? { config: { ...opts.mcpServers && Object.keys(opts.mcpServers).length ? { mcp_servers: opts.mcpServers } : {}, ...opts.extraConfig ?? {} } } : {}
23819
+ });
23820
+ thread = resumeId ? codex.resumeThread(resumeId, threadOptions()) : codex.startThread(threadOptions());
23821
+ return thread;
23822
+ }
23823
+ async function runTurn(prompt, handlers, signal) {
23824
+ const startedAt = Date.now();
23825
+ const usage = emptyUsage();
23826
+ let answer = "";
23827
+ let error;
23828
+ let status = "completed";
23829
+ const finish = () => ({ status, answer, ms: Date.now() - startedAt, usage, error, sessionId: threadId });
23830
+ if (signal?.aborted) {
23831
+ status = "aborted";
23832
+ return finish();
23833
+ }
23834
+ const controller = new AbortController();
23835
+ let earlyExit = false;
23836
+ const onCallerAbort = () => controller.abort();
23837
+ signal?.addEventListener("abort", onCallerAbort, { once: true });
23838
+ let poll = null;
23839
+ let graceTimer = null;
23840
+ if (handlers?.doneWhen) {
23841
+ poll = setInterval(() => {
23842
+ void (async () => {
23843
+ try {
23844
+ if (!await handlers.doneWhen() || earlyExit) return;
23845
+ earlyExit = true;
23846
+ if (poll) {
23847
+ clearInterval(poll);
23848
+ poll = null;
23849
+ }
23850
+ graceTimer = setTimeout(() => controller.abort(), USAGE_GRACE_MS);
23851
+ } catch {
23852
+ }
23853
+ })();
23854
+ }, DONE_POLL_MS);
23855
+ }
23856
+ const emitted = /* @__PURE__ */ new Map();
23857
+ const emit = (chunk, narrate) => {
23858
+ buf = (buf + chunk).slice(-BUFFER_CAP);
23859
+ handlers?.onOutput?.(chunk);
23860
+ if (narrate) handlers?.onNarration?.(chunk);
23861
+ };
23862
+ try {
23863
+ const t = await ensureThread();
23864
+ const streamed = await t.runStreamed(prompt, { signal: controller.signal });
23865
+ for await (const ev of streamed.events) {
23866
+ if (ev.type === "thread.started" && ev.thread_id) threadId = ev.thread_id;
23867
+ if (ev.type === "turn.completed" && ev.usage) {
23868
+ addUsage(usage, ev.usage);
23869
+ handlers?.onUsage?.({ ...usage });
23870
+ if (earlyExit) {
23871
+ if (graceTimer) {
23872
+ clearTimeout(graceTimer);
23873
+ graceTimer = null;
23874
+ }
23875
+ controller.abort();
23876
+ }
23877
+ continue;
23878
+ }
23879
+ if (ev.type === "turn.failed") {
23880
+ error = ev.error?.message ?? "codex turn failed";
23881
+ status = "error";
23882
+ continue;
23883
+ }
23884
+ if (ev.type === "error") {
23885
+ error = ev.message ?? "codex stream error";
23886
+ status = "error";
23887
+ continue;
23888
+ }
23889
+ if (ev.type === "turn.started") {
23890
+ const e = { kind: "turn" };
23891
+ eventLog.push(e);
23892
+ handlers?.onEvent?.(e);
23893
+ continue;
23894
+ }
23895
+ if (ev.type !== "item.started" && ev.type !== "item.updated" && ev.type !== "item.completed") continue;
23896
+ const done = ev.type === "item.completed";
23897
+ const event = normEvent(ev, done);
23898
+ if (!event) continue;
23899
+ eventLog.push(event);
23900
+ if (eventLog.length > EVENT_CAP) eventLog.shift();
23901
+ handlers?.onEvent?.(event);
23902
+ if (done && (event.kind === "tool" || event.kind === "command")) usage.toolCalls++;
23903
+ if (event.kind === "message") {
23904
+ const text = event.text ?? "";
23905
+ const prev = emitted.get(event.id ?? "") ?? 0;
23906
+ if (text.length > prev) {
23907
+ emitted.set(event.id ?? "", text.length);
23908
+ emit(text.slice(prev), true);
23909
+ }
23910
+ if (done && text) answer = text;
23911
+ continue;
23912
+ }
23913
+ const key = `m:${event.id}`;
23914
+ if (!emitted.has(key)) {
23915
+ const marker = markerFor(event);
23916
+ if (marker) {
23917
+ emitted.set(key, 1);
23918
+ emit(`
23919
+ ${marker}
23920
+ `, false);
23921
+ }
23922
+ }
23923
+ }
23924
+ if (earlyExit) status = "early";
23925
+ } catch (err) {
23926
+ if (earlyExit) status = "early";
23927
+ else if (signal?.aborted) status = "aborted";
23928
+ else if (resumeId && !resumeFailed) {
23929
+ resumeFailed = true;
23930
+ resumeId = void 0;
23931
+ thread = null;
23932
+ threadId = void 0;
23933
+ signal?.removeEventListener("abort", onCallerAbort);
23934
+ if (poll) clearInterval(poll);
23935
+ return runTurn(prompt, handlers, signal);
23936
+ } else {
23937
+ status = "error";
23938
+ error = err?.message ?? String(err);
23939
+ }
23940
+ } finally {
23941
+ if (poll) clearInterval(poll);
23942
+ if (graceTimer) clearTimeout(graceTimer);
23943
+ signal?.removeEventListener("abort", onCallerAbort);
23944
+ }
23945
+ return finish();
23946
+ }
23947
+ return {
23948
+ harness: "codex",
23949
+ kind: "events",
23950
+ async run(prompt, handlers, signal) {
23951
+ if (running) throw new Error("This codex session already has a turn in flight.");
23952
+ running = true;
23953
+ try {
23954
+ return await runTurn(prompt, handlers, signal);
23955
+ } finally {
23956
+ running = false;
23957
+ }
23958
+ },
23959
+ events: () => eventLog,
23960
+ buffer: () => buf,
23961
+ busy: () => running,
23962
+ sessionId: () => threadId,
23963
+ reset: () => {
23964
+ thread = null;
23965
+ resumeId = void 0;
23966
+ threadId = void 0;
23967
+ resumeFailed = false;
23968
+ buf = "";
23969
+ eventLog.length = 0;
23970
+ },
23971
+ dispose: () => {
23972
+ thread = null;
23973
+ }
23974
+ };
23975
+ }
23976
+
23977
+ // src/codingAgent/factory.ts
23978
+ function createCodingAgentSession(harness, opts) {
23979
+ switch (harness) {
23980
+ case "codex":
23981
+ return createCodexSession(opts);
23982
+ default:
23983
+ throw new Error(`Unknown coding-agent harness: ${harness}`);
23984
+ }
23985
+ }
23986
+
23987
+ // src/codingAgent/registry.ts
23988
+ var path8 = __toESM(require("path"));
23989
+ var agents = /* @__PURE__ */ new Map();
23990
+ var config = {
23991
+ promptRoot: path8.join(process.cwd(), ".prompts", "coding-agents"),
23992
+ runtimeDir: path8.join(process.cwd(), ".coding-agent-runtime"),
23993
+ harness: "codex",
23994
+ keepRuns: 20
23995
+ };
23996
+ function registerCodingAgents(definitions, options = {}) {
23997
+ config = {
23998
+ ...config,
23999
+ ...options,
24000
+ promptRoot: options.promptRoot || process.env.CODING_AGENT_PROMPT_DIR || config.promptRoot,
24001
+ runtimeDir: options.runtimeDir || process.env.CODING_AGENT_RUNTIME_DIR || config.runtimeDir,
24002
+ harness: options.harness || process.env.CODING_AGENT_HARNESS || config.harness,
24003
+ keepRuns: options.keepRuns ?? config.keepRuns
24004
+ };
24005
+ for (const def of definitions) {
24006
+ if (!def.id) throw new Error("A coding agent definition must have an id.");
24007
+ if (def.mode === "task" && !def.output) {
24008
+ throw new Error(`Coding agent "${def.id}" is mode 'task' but declares no output contract.`);
24009
+ }
24010
+ agents.set(def.id, def);
24011
+ }
24012
+ console.log(`\u2713 coding agents registered: ${[...agents.keys()].join(", ") || "(none)"}`);
24013
+ }
24014
+ function getCodingAgent(id) {
24015
+ return agents.get(id);
24016
+ }
24017
+ function listCodingAgents() {
24018
+ return [...agents.values()].map((a) => ({
24019
+ id: a.id,
24020
+ title: a.title,
24021
+ description: a.description,
24022
+ mode: a.mode
24023
+ }));
24024
+ }
24025
+ function getCodingAgentsConfig() {
24026
+ return config;
24027
+ }
24028
+ function resolvePromptDir(def) {
24029
+ return def.promptDir || path8.join(config.promptRoot, def.id);
24030
+ }
24031
+
24032
+ // src/codingAgent/runner.ts
24033
+ var fs8 = __toESM(require("fs"));
24034
+ var fsp = __toESM(require("fs/promises"));
24035
+ var path9 = __toESM(require("path"));
24036
+ var DEFAULT_TIMEOUT_MS = 15 * 60 * 1e3;
24037
+ var inFlight = /* @__PURE__ */ new Set();
24038
+ function slug(v) {
24039
+ return v.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80) || "unknown";
24040
+ }
24041
+ function resolveScope(def, opts) {
24042
+ const scope = def.scope ?? (def.mode === "chat" ? "thread" : "project");
24043
+ if (scope === "thread") {
24044
+ const id = slug(opts.threadId || "default");
24045
+ return { key: `thread:${id}`, dirName: path9.join("threads", id) };
24046
+ }
24047
+ if (scope === "user") {
24048
+ const id = slug(opts.userId || "anonymous");
24049
+ return { key: `user:${id}`, dirName: path9.join("users", id) };
24050
+ }
24051
+ return { key: "project", dirName: "_project" };
24052
+ }
24053
+ function makeRunId() {
24054
+ const t = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+/, "");
24055
+ return `${t}-${Math.random().toString(36).slice(2, 8)}`;
24056
+ }
24057
+ function statePath(scopeDir) {
24058
+ return path9.join(scopeDir, "_state.json");
24059
+ }
24060
+ function loadState(scopeDir) {
24061
+ try {
24062
+ return JSON.parse(fs8.readFileSync(statePath(scopeDir), "utf-8"));
24063
+ } catch {
24064
+ return { turns: 0, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
24065
+ }
24066
+ }
24067
+ function saveState(scopeDir, state) {
24068
+ try {
24069
+ fs8.mkdirSync(scopeDir, { recursive: true });
24070
+ fs8.writeFileSync(statePath(scopeDir), JSON.stringify({ ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
24071
+ } catch (err) {
24072
+ logger.warn(`[CodingAgent] failed to persist session state: ${err?.message ?? err}`);
24073
+ }
24074
+ }
24075
+ async function clearTransient(dir) {
24076
+ await fsp.rm(path9.join(dir, "input"), { recursive: true, force: true });
24077
+ }
24078
+ function createWorkspace(dir, runId) {
24079
+ const abs = (rel) => path9.join(dir, rel);
24080
+ const ensureDir = async (rel) => {
24081
+ await fsp.mkdir(path9.dirname(abs(rel)), { recursive: true });
24082
+ };
24083
+ return {
24084
+ dir,
24085
+ runId,
24086
+ pathTo: abs,
24087
+ async write(rel, contents) {
24088
+ await ensureDir(rel);
24089
+ await fsp.writeFile(abs(rel), contents, "utf-8");
24090
+ },
24091
+ async writeJson(rel, value) {
24092
+ await ensureDir(rel);
24093
+ await fsp.writeFile(abs(rel), JSON.stringify(value, null, 2), "utf-8");
24094
+ },
24095
+ async writeJsonl(rel, rows) {
24096
+ await ensureDir(rel);
24097
+ await fsp.writeFile(abs(rel), rows.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf-8");
24098
+ },
24099
+ async read(rel) {
24100
+ try {
24101
+ return await fsp.readFile(abs(rel), "utf-8");
24102
+ } catch {
24103
+ return null;
24104
+ }
24105
+ }
24106
+ };
24107
+ }
24108
+ async function installInstructions(def, ws) {
24109
+ const src = path9.join(resolvePromptDir(def), "system.md");
24110
+ try {
24111
+ await ws.write("AGENTS.md", await fsp.readFile(src, "utf-8"));
24112
+ } catch {
24113
+ logger.warn(`[CodingAgent:${def.id}] no system.md at ${src} \u2014 running without standing instructions`);
24114
+ }
24115
+ }
24116
+ async function pruneRuns(agentDir, keep) {
24117
+ if (keep <= 0) return;
24118
+ try {
24119
+ const entries = (await fsp.readdir(agentDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
24120
+ for (const name of entries.slice(0, Math.max(0, entries.length - keep))) {
24121
+ await fsp.rm(path9.join(agentDir, name), { recursive: true, force: true });
24122
+ }
24123
+ } catch {
24124
+ }
24125
+ }
24126
+ async function runCodingAgent(agentId, options = {}) {
24127
+ const def = getCodingAgent(agentId);
24128
+ if (!def) {
24129
+ const known = listCodingAgents().map((a) => a.id);
24130
+ throw new Error(
24131
+ `Unknown coding agent "${agentId}". Registered: ${known.join(", ") || "(none \u2014 was registerCodingAgents called?)"}`
24132
+ );
24133
+ }
24134
+ const cfg = getCodingAgentsConfig();
24135
+ const runId = makeRunId();
24136
+ const agentDir = path9.join(cfg.runtimeDir, def.id);
24137
+ const policy = def.session ?? {};
24138
+ const persistentWorkspace = policy.workspace === "persistent";
24139
+ const persistentSession = policy.scope === "persistent";
24140
+ const { key: scopeKey, dirName } = resolveScope(def, { userId: options.userId, threadId: options.threadId });
24141
+ const scopeDir = path9.join(agentDir, dirName);
24142
+ const dir = options.workspaceDir || (persistentWorkspace ? scopeDir : path9.join(scopeDir, "runs", runId));
24143
+ fs8.mkdirSync(dir, { recursive: true });
24144
+ if (persistentWorkspace && !options.workspaceDir) {
24145
+ fs8.mkdirSync(path9.join(dir, "memory"), { recursive: true });
24146
+ await clearTransient(dir);
24147
+ }
24148
+ const lockKey = persistentWorkspace || persistentSession ? `${def.id}:${scopeKey}` : `dir:${dir}`;
24149
+ if (inFlight.has(lockKey)) {
24150
+ logger.info(`[CodingAgent:${def.id}] already running \u2014 skipping this run`);
24151
+ return {
24152
+ agentId: def.id,
24153
+ runId,
24154
+ workspaceDir: dir,
24155
+ status: "skipped",
24156
+ answer: "",
24157
+ ms: 0,
24158
+ usage: { inputTokens: 0, outputTokens: 0, reasoningTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, toolCalls: 0 },
24159
+ error: "a run for this agent is already in progress"
24160
+ };
24161
+ }
24162
+ inFlight.add(lockKey);
24163
+ const state = loadState(scopeDir);
24164
+ let resumeId = options.resumeId;
24165
+ let turns = state.turns;
24166
+ if (persistentSession && !resumeId) {
24167
+ const overThreshold = policy.resetAfterTurns !== void 0 && state.turns >= policy.resetAfterTurns;
24168
+ if (overThreshold) {
24169
+ logger.info(
24170
+ `[CodingAgent:${def.id}] session reset after ${state.turns} runs (resetAfterTurns=${policy.resetAfterTurns}) \u2014 memory/ carries over`
24171
+ );
24172
+ turns = 0;
24173
+ } else {
24174
+ resumeId = state.sessionId;
24175
+ }
24176
+ }
24177
+ const ws = createWorkspace(dir, runId);
24178
+ const ctx = {
24179
+ projectId: options.projectId,
24180
+ userId: options.userId,
24181
+ runId,
24182
+ workspaceDir: dir,
24183
+ signal: options.signal,
24184
+ onOutput: options.onOutput,
24185
+ onEvent: options.onEvent,
24186
+ onUsage: options.onUsage
24187
+ };
24188
+ const input = options.input ?? {};
24189
+ const startedAt = Date.now();
24190
+ const record = {
24191
+ agentId: def.id,
24192
+ runId,
24193
+ workspaceDir: dir,
24194
+ status: "error",
24195
+ answer: "",
24196
+ ms: 0,
24197
+ usage: { inputTokens: 0, outputTokens: 0, reasoningTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, toolCalls: 0 }
24198
+ };
24199
+ try {
24200
+ await installInstructions(def, ws);
24201
+ if (def.prepare) await def.prepare(ws, input, ctx);
24202
+ const outputPath = def.output ? ws.pathTo(def.output.file) : void 0;
24203
+ if (outputPath) await fsp.rm(outputPath, { force: true });
24204
+ const prompt = options.prompt ?? (def.buildPrompt ? await def.buildPrompt(ws, input, ctx) : outputPath ? `Carry out the task described in AGENTS.md. Write your finished result to this exact absolute path: ${outputPath}` : "Carry out the task described in AGENTS.md.");
24205
+ const tools = { ...cfg.sharedTools ?? {}, ...def.tools ?? {} };
24206
+ const session = createCodingAgentSession(def.harness || cfg.harness, {
24207
+ cwd: dir,
24208
+ model: def.model || cfg.model,
24209
+ resumeId,
24210
+ // undefined, not {}, when this agent has no tools — see the driver.
24211
+ mcpServers: tools && Object.keys(tools).length ? tools : void 0,
24212
+ ...def.networkAccessEnabled !== void 0 ? { networkAccessEnabled: def.networkAccessEnabled } : {},
24213
+ ...def.sandboxMode ? { sandboxMode: def.sandboxMode } : {}
24214
+ });
24215
+ const timeout = new AbortController();
24216
+ const timer = setTimeout(() => timeout.abort(), def.timeoutMs ?? DEFAULT_TIMEOUT_MS);
24217
+ const onOuterAbort = () => timeout.abort();
24218
+ options.signal?.addEventListener("abort", onOuterAbort, { once: true });
24219
+ let result;
24220
+ try {
24221
+ result = await session.run(
24222
+ prompt,
24223
+ {
24224
+ onOutput: ctx.onOutput,
24225
+ onEvent: ctx.onEvent,
24226
+ onUsage: ctx.onUsage,
24227
+ // Opt-in only: see stopOnDeliverable. Letting the turn end naturally
24228
+ // is what makes usage reporting work at all.
24229
+ ...outputPath && def.stopOnDeliverable ? { doneWhen: () => fs8.existsSync(outputPath) } : {}
24230
+ },
24231
+ timeout.signal
24232
+ );
24233
+ } finally {
24234
+ clearTimeout(timer);
24235
+ options.signal?.removeEventListener("abort", onOuterAbort);
24236
+ session.dispose();
24237
+ }
24238
+ record.answer = result.answer;
24239
+ record.ms = Date.now() - startedAt;
24240
+ record.usage = result.usage;
24241
+ record.sessionId = result.sessionId;
24242
+ if (persistentSession && result.sessionId) {
24243
+ saveState(scopeDir, { sessionId: result.sessionId, turns: turns + 1, updatedAt: "" });
24244
+ }
24245
+ if (result.status === "aborted") {
24246
+ record.status = "aborted";
24247
+ record.error = options.signal?.aborted ? "aborted" : "timed out";
24248
+ return record;
24249
+ }
24250
+ if (result.status === "error") {
24251
+ record.error = result.error ?? "agent failed";
24252
+ return record;
24253
+ }
24254
+ const successStatus = result.status === "early" ? "early" : "completed";
24255
+ if (!def.output) {
24256
+ record.status = successStatus;
24257
+ return record;
24258
+ }
24259
+ const raw = await ws.read(def.output.file);
24260
+ if (raw === null) {
24261
+ record.error = `agent finished without writing ${def.output.file}`;
24262
+ return record;
24263
+ }
24264
+ let parsed;
24265
+ try {
24266
+ parsed = def.output.schema.parse(JSON.parse(raw));
24267
+ } catch (err) {
24268
+ record.error = `deliverable failed validation: ${err?.message ?? err}`;
24269
+ return record;
24270
+ }
24271
+ record.output = parsed;
24272
+ if (def.apply) {
24273
+ record.applied = await def.apply(parsed, input, ctx);
24274
+ }
24275
+ record.status = successStatus;
24276
+ return record;
24277
+ } catch (err) {
24278
+ record.error = err?.message ?? String(err);
24279
+ record.ms = Date.now() - startedAt;
24280
+ return record;
24281
+ } finally {
24282
+ inFlight.delete(lockKey);
24283
+ logger.info(
24284
+ `[CodingAgent:${def.id}] run=${runId} status=${record.status} ms=${record.ms} tokens=${record.usage.inputTokens}/${record.usage.outputTokens} tools=${record.usage.toolCalls}` + (record.applied ? ` applied="${record.applied.summary}"` : "") + (record.error ? ` error="${record.error}"` : "")
24285
+ );
24286
+ if (!options.workspaceDir && !persistentWorkspace) await pruneRuns(path9.join(scopeDir, "runs"), cfg.keepRuns);
24287
+ }
24288
+ }
24289
+
24290
+ // src/codingAgent/tools/bridge-shims.ts
24291
+ var fs9 = __toESM(require("fs"));
24292
+
24293
+ // src/codingAgent/tools/read-only-sql.ts
24294
+ var WRITE_KEYWORDS = [
24295
+ "insert",
24296
+ "update",
24297
+ "delete",
24298
+ "drop",
24299
+ "alter",
24300
+ "create",
24301
+ "truncate",
24302
+ "replace",
24303
+ "grant",
24304
+ "revoke",
24305
+ "merge",
24306
+ "upsert",
24307
+ "call",
24308
+ "exec",
24309
+ "execute",
24310
+ "copy",
24311
+ "vacuum",
24312
+ "analyze",
24313
+ "comment",
24314
+ "lock",
24315
+ "set",
24316
+ "commit",
24317
+ "rollback",
24318
+ "begin"
24319
+ ];
24320
+ function stripSqlNoise(sql) {
24321
+ return sql.replace(/--[^\n]*/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/'(?:[^']|'')*'/g, "''").replace(/"(?:[^"]|"")*"/g, '""');
24322
+ }
24323
+ function assertReadOnlySql(sql) {
24324
+ const bare = stripSqlNoise(sql).trim();
24325
+ if (!bare) throw new Error("Empty SQL.");
24326
+ if (bare.replace(/;\s*$/, "").includes(";")) {
24327
+ throw new Error('Only a single statement is allowed \u2014 remove the extra ";".');
24328
+ }
24329
+ if (!/^\s*(select|with)\b/i.test(bare)) {
24330
+ throw new Error("Read-only: the statement must start with SELECT or WITH.");
24331
+ }
24332
+ if (/\binto\s+\w/i.test(bare)) {
24333
+ throw new Error("Read-only: SELECT ... INTO is not allowed.");
24334
+ }
24335
+ for (const kw of WRITE_KEYWORDS) {
24336
+ if (new RegExp(`\\b${kw}\\b`, "i").test(bare)) {
24337
+ throw new Error(`Read-only: "${kw.toUpperCase()}" is not allowed. Only SELECT / WITH queries.`);
24338
+ }
24339
+ }
24340
+ }
24341
+ var READ_ONLY_SQL_SOURCE = `
24342
+ const WRITE_KEYWORDS = ${JSON.stringify(WRITE_KEYWORDS)};
24343
+ ${stripSqlNoise.toString()}
24344
+ ${assertReadOnlySql.toString().replace("export function", "function")}
24345
+ `;
24346
+
24347
+ // src/codingAgent/tools/bridge-shims.ts
24348
+ function writeBridgeShims(ws, opts) {
24349
+ const maxRows = opts.maxRows ?? 200;
24350
+ const query = `#!/usr/bin/env node
24351
+ // Read-only SQL against a data source. Generated per run \u2014 do not edit.
24352
+ ${READ_ONLY_SQL_SOURCE}
24353
+ const BRIDGE = ${JSON.stringify(opts.bridgeUrl)};
24354
+ const MAX_ROWS = ${maxRows};
24355
+
24356
+ const [toolId, sql, limitArg] = process.argv.slice(2);
24357
+ if (!toolId || !sql) {
24358
+ console.error('usage: ./query "<toolId>" "<sql>" [limit]\\nRun ./sources for the toolIds.');
24359
+ process.exit(2);
24360
+ }
24361
+ try { assertReadOnlySql(sql); } catch (e) { console.error(String(e.message || e)); process.exit(2); }
24362
+
24363
+ fetch(BRIDGE + '/query', {
24364
+ method: 'POST',
24365
+ headers: { 'content-type': 'application/json' },
24366
+ body: JSON.stringify({ toolId, sql, limit: Math.min(Number(limitArg) || MAX_ROWS, MAX_ROWS) }),
24367
+ })
24368
+ .then(async (r) => {
24369
+ const text = await r.text();
24370
+ if (!r.ok) { console.error('Query failed (' + r.status + '): ' + text.slice(0, 2000)); process.exit(1); }
24371
+ let p; try { p = JSON.parse(text); } catch { console.log(text); return; }
24372
+ if (p && p.error) { console.error(String(p.error)); process.exit(1); }
24373
+ const rows = (p && (p.data || p.rows)) || p;
24374
+ const all = Array.isArray(rows) ? rows : [rows];
24375
+ // Capped so one careless SELECT * cannot flood the agent's context.
24376
+ console.log(JSON.stringify({
24377
+ count: (p && p.count) != null ? p.count : all.length,
24378
+ truncated: all.length > MAX_ROWS,
24379
+ rows: all.slice(0, MAX_ROWS),
24380
+ }, null, 2));
24381
+ })
24382
+ .catch((e) => { console.error('Could not reach the data bridge: ' + (e && e.message)); process.exit(1); });
24383
+ `;
24384
+ const sources = `#!/usr/bin/env node
24385
+ // The data sources you can query, and the exact toolId for ./query.
24386
+ console.log(${JSON.stringify(JSON.stringify(opts.sources, null, 2))});
24387
+ `;
24388
+ for (const [name, body] of [["query", query], ["sources", sources]]) {
24389
+ const p = ws.pathTo(name);
24390
+ fs9.writeFileSync(p, body, "utf-8");
24391
+ fs9.chmodSync(p, 493);
24392
+ }
24393
+ }
24394
+
24395
+ // src/codingAgent/triggers.ts
24396
+ var fs10 = __toESM(require("fs"));
24397
+ var path10 = __toESM(require("path"));
24398
+ function statePath2(agentId) {
24399
+ return path10.join(getCodingAgentsConfig().runtimeDir, agentId, "_triggers.json");
24400
+ }
24401
+ function loadTriggerState(agentId) {
24402
+ try {
24403
+ return JSON.parse(fs10.readFileSync(statePath2(agentId), "utf-8"));
24404
+ } catch {
24405
+ return { counts: {} };
24406
+ }
24407
+ }
24408
+ function saveTriggerState(agentId, state) {
24409
+ try {
24410
+ const p = statePath2(agentId);
24411
+ fs10.mkdirSync(path10.dirname(p), { recursive: true });
24412
+ fs10.writeFileSync(p, JSON.stringify(state, null, 2));
24413
+ } catch (err) {
24414
+ logger.warn(`[CodingAgent:${agentId}] failed to persist trigger state: ${err?.message ?? err}`);
24415
+ }
24416
+ }
24417
+ var asArray = (t) => !t ? [] : Array.isArray(t) ? t : [t];
24418
+ var intervals = /* @__PURE__ */ new Map();
24419
+ var debounces = /* @__PURE__ */ new Map();
24420
+ var firing = /* @__PURE__ */ new Set();
24421
+ var listeners = [];
24422
+ function onAgentRun(fn) {
24423
+ listeners.push(fn);
24424
+ return () => {
24425
+ const i = listeners.indexOf(fn);
24426
+ if (i >= 0) listeners.splice(i, 1);
24427
+ };
24428
+ }
24429
+ async function fire(agentId, reason, payload) {
24430
+ if (firing.has(agentId)) {
24431
+ const st = loadTriggerState(agentId);
24432
+ saveTriggerState(agentId, { ...st, pending: true });
24433
+ logger.info(`[CodingAgent:${agentId}] ${reason} arrived mid-run \u2014 queued`);
24434
+ return;
24435
+ }
24436
+ firing.add(agentId);
24437
+ try {
24438
+ logger.info(`[CodingAgent:${agentId}] triggered by ${reason}`);
24439
+ const record = await runCodingAgent(agentId, { input: payload });
24440
+ saveTriggerState(agentId, { ...loadTriggerState(agentId), lastRunAt: (/* @__PURE__ */ new Date()).toISOString(), pending: false });
24441
+ for (const fn of listeners) {
24442
+ try {
24443
+ fn(record, { agentId, reason, payload });
24444
+ } catch {
24445
+ }
24446
+ }
24447
+ } catch (err) {
24448
+ logger.error(`[CodingAgent:${agentId}] trigger run failed: ${err?.message ?? err}`);
24449
+ } finally {
24450
+ firing.delete(agentId);
24451
+ if (loadTriggerState(agentId).pending) {
24452
+ saveTriggerState(agentId, { ...loadTriggerState(agentId), pending: false });
24453
+ void fire(agentId, "queued work from during the last run");
24454
+ }
24455
+ }
24456
+ }
24457
+ function emitAgentEvent(event, payload) {
24458
+ for (const { id } of listCodingAgents()) {
24459
+ const def = getCodingAgent(id);
24460
+ if (!def) continue;
24461
+ for (const trigger of asArray(def.trigger)) {
24462
+ if (trigger.type === "event") {
24463
+ const names = Array.isArray(trigger.on) ? trigger.on : [trigger.on];
24464
+ if (!names.includes(event)) continue;
24465
+ if (!trigger.debounceMs) {
24466
+ void fire(id, `event ${event}`, payload);
24467
+ continue;
24468
+ }
24469
+ const key = `${id}:${event}`;
24470
+ const existing = debounces.get(key);
24471
+ if (existing) clearTimeout(existing);
24472
+ debounces.set(key, setTimeout(() => {
24473
+ debounces.delete(key);
24474
+ void fire(id, `event ${event} (settled)`, payload);
24475
+ }, trigger.debounceMs));
24476
+ continue;
24477
+ }
24478
+ if (trigger.type === "count" && trigger.on === event) {
24479
+ const state = loadTriggerState(id);
24480
+ const next = (state.counts[event] ?? 0) + 1;
24481
+ if (next < trigger.every) {
24482
+ saveTriggerState(id, { ...state, counts: { ...state.counts, [event]: next } });
24483
+ continue;
24484
+ }
24485
+ saveTriggerState(id, { ...state, counts: { ...state.counts, [event]: 0 } });
24486
+ void fire(id, `${trigger.every}x ${event}`, payload);
24487
+ }
24488
+ }
24489
+ }
24490
+ }
24491
+ function startAgentTriggers() {
24492
+ stopAgentTriggers();
24493
+ for (const { id } of listCodingAgents()) {
24494
+ const def = getCodingAgent(id);
24495
+ for (const trigger of asArray(def?.trigger)) {
24496
+ if (trigger.type !== "interval") continue;
24497
+ intervals.set(id, setInterval(() => void fire(id, `interval ${trigger.everyMs}ms`), trigger.everyMs));
24498
+ logger.info(`[CodingAgent:${id}] interval trigger every ${trigger.everyMs}ms`);
24499
+ }
24500
+ }
24501
+ }
24502
+ function stopAgentTriggers() {
24503
+ for (const t of intervals.values()) clearInterval(t);
24504
+ intervals.clear();
24505
+ for (const t of debounces.values()) clearTimeout(t);
24506
+ debounces.clear();
24507
+ }
24508
+ function describeTriggers() {
24509
+ return listCodingAgents().map(({ id }) => {
24510
+ const def = getCodingAgent(id);
24511
+ return {
24512
+ agentId: id,
24513
+ triggers: asArray(def?.trigger).map(
24514
+ (t) => t.type === "interval" ? `every ${t.everyMs}ms` : t.type === "event" ? `on ${[t.on].flat().join(", ")}${t.debounceMs ? ` (debounce ${t.debounceMs}ms)` : ""}` : t.type === "count" ? `every ${t.every}x ${t.on}` : "manual"
24515
+ )
24516
+ };
24517
+ });
24518
+ }
24519
+
24520
+ // src/codingAgent/collection.ts
24521
+ var fs11 = __toESM(require("fs"));
24522
+ var path11 = __toESM(require("path"));
24523
+ function registerCodingAgentCollection(sdk, config2 = {}) {
24524
+ const runtimeDir = config2.runtimeDir || process.env.CODING_AGENT_RUNTIME_DIR || path11.join(process.cwd(), ".coding-agent-runtime");
24525
+ const defaultHarness = config2.harness || process.env.CODING_AGENT_HARNESS || "codex";
24526
+ const sessions = /* @__PURE__ */ new Map();
24527
+ const running = /* @__PURE__ */ new Set();
24528
+ sdk.addCollection("coding-agent", "run", async (params) => {
24529
+ const { prompt, threadId, agentId, projectId, userId, harness, resumeId, signal, onOutput, onEvent, onUsage } = params;
24530
+ if (!prompt || !threadId) {
24531
+ return { success: false, error: "prompt and threadId are required" };
24532
+ }
24533
+ if (running.has(threadId)) {
24534
+ return { success: false, error: "This thread already has a turn in progress." };
24535
+ }
24536
+ running.add(threadId);
24537
+ try {
24538
+ if (agentId) {
24539
+ const def = getCodingAgent(agentId);
24540
+ if (!def) {
24541
+ return {
24542
+ success: false,
24543
+ error: `Unknown agent "${agentId}". Available: ${listCodingAgents().map((a) => a.id).join(", ") || "(none registered)"}`
24544
+ };
24545
+ }
24546
+ const record = await runCodingAgent(agentId, {
24547
+ prompt,
24548
+ projectId,
24549
+ userId,
24550
+ threadId,
24551
+ signal,
24552
+ onOutput,
24553
+ onEvent,
24554
+ onUsage,
24555
+ ...resumeId ? { resumeId } : {}
24556
+ });
24557
+ return {
24558
+ success: record.status === "completed" || record.status === "early",
24559
+ status: record.status,
24560
+ answer: record.answer,
24561
+ ms: record.ms,
24562
+ usage: record.usage,
24563
+ sessionId: record.sessionId,
24564
+ ...record.output !== void 0 ? { output: record.output } : {},
24565
+ ...record.applied ? { applied: record.applied } : {},
24566
+ ...record.error ? { error: record.error } : {}
24567
+ };
24568
+ }
24569
+ const cwd = path11.join(runtimeDir, "threads", threadId);
24570
+ fs11.mkdirSync(cwd, { recursive: true });
24571
+ let session = sessions.get(threadId);
24572
+ if (!session) {
24573
+ session = createCodingAgentSession(harness || defaultHarness, {
24574
+ cwd,
24575
+ model: config2.model,
24576
+ resumeId,
24577
+ mcpServers: config2.mcpServers
24578
+ });
24579
+ sessions.set(threadId, session);
24580
+ }
24581
+ const result = await session.run(prompt, { onOutput, onEvent, onUsage }, signal);
24582
+ logger.info(
24583
+ `[CodingAgent] thread=${threadId} harness=${session.harness} status=${result.status} ms=${result.ms} tokens=${result.usage.inputTokens}/${result.usage.outputTokens} tools=${result.usage.toolCalls}`
24584
+ );
24585
+ return {
24586
+ // An abort is the user's own choice, not a failure — but it is also
24587
+ // not a completed answer, so `status` is what the UI should branch on.
24588
+ success: result.status === "completed" || result.status === "early",
24589
+ status: result.status,
24590
+ answer: result.answer,
24591
+ ms: result.ms,
24592
+ usage: result.usage,
24593
+ sessionId: result.sessionId,
24594
+ ...result.error ? { error: result.error } : {}
24595
+ };
24596
+ } catch (error) {
24597
+ const message = error?.message ?? String(error);
24598
+ logger.error(`[CodingAgent] thread=${threadId} failed: ${message}`);
24599
+ return { success: false, status: "error", error: message };
24600
+ } finally {
24601
+ running.delete(threadId);
24602
+ }
24603
+ });
24604
+ sdk.addCollection("coding-agent", "reset", async (params) => {
24605
+ const session = sessions.get(params?.threadId);
24606
+ if (session) {
24607
+ session.reset();
24608
+ session.dispose();
24609
+ sessions.delete(params.threadId);
24610
+ }
24611
+ return { success: true };
24612
+ });
24613
+ sdk.addCollection("coding-agent", "list", async () => ({ success: true, agents: listCodingAgents() }));
24614
+ console.log(`\u2713 coding-agent collection registered (harness: ${defaultHarness})`);
24615
+ }
24616
+
24617
+ // src/codingAgent/handler.ts
24618
+ var inFlightAborts3 = /* @__PURE__ */ new Map();
24619
+ var recentlyAborted3 = /* @__PURE__ */ new Map();
24620
+ var ABORT_TOMBSTONE_TTL_MS3 = 3e4;
24621
+ function setAbortTombstone3(threadId) {
24622
+ const now = Date.now();
24623
+ recentlyAborted3.set(threadId, now);
24624
+ for (const [key, ts] of recentlyAborted3) {
24625
+ if (now - ts > ABORT_TOMBSTONE_TTL_MS3) recentlyAborted3.delete(key);
24626
+ }
24627
+ }
24628
+ function consumeAbortTombstone3(threadId) {
24629
+ const ts = recentlyAborted3.get(threadId);
24630
+ if (ts === void 0) return false;
24631
+ recentlyAborted3.delete(threadId);
24632
+ return Date.now() - ts <= ABORT_TOMBSTONE_TTL_MS3;
24633
+ }
24634
+ function handleCodingAgentAbort(data, sendMessage) {
24635
+ const parseResult = CodingAgentAbortRequestMessageSchema.safeParse(data);
24636
+ if (!parseResult.success) {
24637
+ logger.warn("[CodingAgentAbort] Invalid message \u2014 ignoring");
24638
+ return;
24639
+ }
24640
+ const { payload, from } = parseResult.data;
24641
+ const { threadId } = payload;
24642
+ const controller = inFlightAborts3.get(threadId);
24643
+ if (controller) {
24644
+ controller.abort();
24645
+ inFlightAborts3.delete(threadId);
24646
+ logger.info(`[CodingAgentAbort] Aborted in-flight run for thread ${threadId}`);
24647
+ } else {
24648
+ setAbortTombstone3(threadId);
24649
+ logger.info(`[CodingAgentAbort] No in-flight run for thread ${threadId} \u2014 tombstoned`);
24650
+ }
24651
+ try {
24652
+ sendMessage({
24653
+ id: `coding_agent_aborted_${threadId}`,
24654
+ type: "CODING_AGENT_ABORTED",
24655
+ from: { type: "data-agent" },
24656
+ to: { type: "runtime", id: from.id },
24657
+ payload: { threadId }
24658
+ });
24659
+ } catch (err) {
24660
+ logger.warn(`[CodingAgentAbort] Failed to send abort ack: ${err instanceof Error ? err.message : String(err)}`);
24661
+ }
24662
+ }
24663
+ async function handleCodingAgentRequest(data, collections, sendMessage) {
24664
+ const parseResult = CodingAgentRequestMessageSchema.safeParse(data);
24665
+ if (!parseResult.success) {
24666
+ logger.error("[CodingAgentReq] Invalid message:", JSON.stringify(parseResult.error.errors));
24667
+ return;
24668
+ }
24669
+ const { id, payload, from } = parseResult.data;
24670
+ const { prompt, threadId, projectId, userId, agentId, harness, resumeId } = payload;
24671
+ const wsId = from.id;
24672
+ const respond = (res) => {
24673
+ sendMessage({
24674
+ id,
24675
+ type: "CODING_AGENT_RES",
24676
+ from: { type: "data-agent" },
24677
+ to: { type: "runtime", id: wsId },
24678
+ payload: res
24679
+ });
24680
+ };
24681
+ if (!collections?.["coding-agent"]?.["run"]) {
24682
+ respond({ success: false, error: "Coding agent is not available in this environment (collection 'coding-agent' not registered)." });
24683
+ return;
24684
+ }
24685
+ const abortController = new AbortController();
24686
+ if (consumeAbortTombstone3(threadId)) {
24687
+ abortController.abort();
24688
+ logger.info(`[CodingAgentReq] thread ${threadId} was pre-aborted \u2014 starting cancelled`);
24689
+ }
24690
+ inFlightAborts3.set(threadId, abortController);
24691
+ let seq = 0;
24692
+ const stream = (frame) => {
24693
+ try {
24694
+ sendMessage({
24695
+ // Keyed by this request's id, not threadId — threadId repeats across
24696
+ // every turn in a chat, so a listener that failed to clean up would
24697
+ // keep matching future turns forever.
24698
+ id: `coding_agent_stream_${id}`,
24699
+ type: "CODING_AGENT_STREAM",
24700
+ from: { type: "data-agent" },
24701
+ to: { type: "runtime", id: wsId },
24702
+ payload: { threadId, seq: seq++, ...frame }
24703
+ });
24704
+ } catch (err) {
24705
+ logger.warn(`[CodingAgentReq] Stream send failed (socket may be closed): ${err instanceof Error ? err.message : String(err)}`);
24706
+ }
24707
+ };
24708
+ try {
24709
+ const result = await collections["coding-agent"]["run"]({
24710
+ prompt,
24711
+ threadId,
24712
+ projectId,
24713
+ userId,
24714
+ agentId,
24715
+ harness,
24716
+ resumeId,
24717
+ signal: abortController.signal,
24718
+ onOutput: (chunk) => stream({ chunk }),
24719
+ onEvent: (event) => stream({ event }),
24720
+ onUsage: (usage) => stream({ usage })
24721
+ });
24722
+ respond(result ?? { success: false, error: "Coding agent returned no result" });
24723
+ } catch (error) {
24724
+ const errorMsg = error instanceof Error ? error.message : String(error);
24725
+ logger.error(`[CodingAgentReq] Error: ${errorMsg}`);
24726
+ respond({ success: false, error: abortController.signal.aborted ? "aborted" : errorMsg });
24727
+ } finally {
24728
+ inFlightAborts3.delete(threadId);
24729
+ }
24730
+ }
24731
+
23661
24732
  // src/reportComp/types.ts
23662
24733
  var DEFAULT_REPORT_COMP_MODELS = {
23663
24734
  anthropic: "anthropic/claude-haiku-4-5-20251001",
@@ -24156,21 +25227,21 @@ Fixed SQL query:`;
24156
25227
  }
24157
25228
 
24158
25229
  // src/reportComp/index.ts
24159
- var inFlightAborts3 = /* @__PURE__ */ new Map();
24160
- var recentlyAborted3 = /* @__PURE__ */ new Map();
24161
- var ABORT_TOMBSTONE_TTL_MS3 = 3e4;
24162
- function setAbortTombstone3(reportId) {
25230
+ var inFlightAborts4 = /* @__PURE__ */ new Map();
25231
+ var recentlyAborted4 = /* @__PURE__ */ new Map();
25232
+ var ABORT_TOMBSTONE_TTL_MS4 = 3e4;
25233
+ function setAbortTombstone4(reportId) {
24163
25234
  const now = Date.now();
24164
- recentlyAborted3.set(reportId, now);
24165
- for (const [key, ts] of recentlyAborted3) {
24166
- if (now - ts > ABORT_TOMBSTONE_TTL_MS3) recentlyAborted3.delete(key);
25235
+ recentlyAborted4.set(reportId, now);
25236
+ for (const [key, ts] of recentlyAborted4) {
25237
+ if (now - ts > ABORT_TOMBSTONE_TTL_MS4) recentlyAborted4.delete(key);
24167
25238
  }
24168
25239
  }
24169
- function consumeAbortTombstone3(reportId) {
24170
- const ts = recentlyAborted3.get(reportId);
25240
+ function consumeAbortTombstone4(reportId) {
25241
+ const ts = recentlyAborted4.get(reportId);
24171
25242
  if (ts === void 0) return false;
24172
- recentlyAborted3.delete(reportId);
24173
- return Date.now() - ts <= ABORT_TOMBSTONE_TTL_MS3;
25243
+ recentlyAborted4.delete(reportId);
25244
+ return Date.now() - ts <= ABORT_TOMBSTONE_TTL_MS4;
24174
25245
  }
24175
25246
  function handleReportCompAbort(data, sendMessage) {
24176
25247
  const parseResult = ReportCompAbortRequestMessageSchema.safeParse(data);
@@ -24181,13 +25252,13 @@ function handleReportCompAbort(data, sendMessage) {
24181
25252
  const { payload, from } = parseResult.data;
24182
25253
  const { reportId } = payload;
24183
25254
  const wsId = from.id;
24184
- const controller = inFlightAborts3.get(reportId);
25255
+ const controller = inFlightAborts4.get(reportId);
24185
25256
  if (controller) {
24186
25257
  controller.abort();
24187
- inFlightAborts3.delete(reportId);
25258
+ inFlightAborts4.delete(reportId);
24188
25259
  logger.info(`[ReportCompAbort] Aborted in-flight run for report ${reportId}`);
24189
25260
  } else {
24190
- setAbortTombstone3(reportId);
25261
+ setAbortTombstone4(reportId);
24191
25262
  logger.info(`[ReportCompAbort] No in-flight run for report ${reportId} \u2014 tombstoned`);
24192
25263
  }
24193
25264
  try {
@@ -24233,11 +25304,11 @@ var processReportCompRequest = async (data, components, _sendMessage, anthropicA
24233
25304
  const trace = createRunTrace();
24234
25305
  const abortController = new AbortController();
24235
25306
  if (reportId) {
24236
- if (consumeAbortTombstone3(reportId)) {
25307
+ if (consumeAbortTombstone4(reportId)) {
24237
25308
  abortController.abort();
24238
25309
  logger.info(`[REPORT_COMP_REQ] reportId=${reportId} was pre-aborted \u2014 starting cancelled`);
24239
25310
  }
24240
- inFlightAborts3.set(reportId, abortController);
25311
+ inFlightAborts4.set(reportId, abortController);
24241
25312
  }
24242
25313
  let llmResponse;
24243
25314
  try {
@@ -24258,7 +25329,7 @@ var processReportCompRequest = async (data, components, _sendMessage, anthropicA
24258
25329
  trace
24259
25330
  );
24260
25331
  } finally {
24261
- if (reportId) inFlightAborts3.delete(reportId);
25332
+ if (reportId) inFlightAborts4.delete(reportId);
24262
25333
  }
24263
25334
  const wasAborted = abortController.signal.aborted || !llmResponse.success && Array.isArray(llmResponse.errors) && llmResponse.errors.includes("aborted");
24264
25335
  if (wasAborted) {
@@ -24459,7 +25530,7 @@ function pick(source, keys) {
24459
25530
  }
24460
25531
  return out;
24461
25532
  }
24462
- function asArray(value) {
25533
+ function asArray2(value) {
24463
25534
  return Array.isArray(value) ? value : [];
24464
25535
  }
24465
25536
  function sanitizeField(field) {
@@ -24520,7 +25591,7 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24520
25591
  lines.push(`Row Count: ~${entity.rowCount.toLocaleString()}`);
24521
25592
  }
24522
25593
  lines.push("Columns:");
24523
- for (const field of asArray(entity.fields).concat(asArray(entity.columns))) {
25594
+ for (const field of asArray2(entity.fields).concat(asArray2(entity.columns))) {
24524
25595
  let line = ` - ${field.name}: ${field.type}`;
24525
25596
  if (field.isPrimaryKey) line += " (PRIMARY KEY)";
24526
25597
  if (field.isForeignKey && field.references) {
@@ -24538,7 +25609,7 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24538
25609
  lines.push("TABLE RELATIONSHIPS:");
24539
25610
  lines.push("");
24540
25611
  for (const rel of relationships) {
24541
- lines.push(`${rel.from} -> ${rel.to} (${rel.type}): ${asArray(rel.keys).join(" = ")}`);
25612
+ lines.push(`${rel.from} -> ${rel.to} (${rel.type}): ${asArray2(rel.keys).join(" = ")}`);
24542
25613
  }
24543
25614
  lines.push("");
24544
25615
  };
@@ -24546,10 +25617,10 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24546
25617
  if (sources) {
24547
25618
  for (const source of sources) {
24548
25619
  renderEntities(
24549
- asArray(source.schema?.entities).concat(asArray(source.schema?.tables)),
25620
+ asArray2(source.schema?.entities).concat(asArray2(source.schema?.tables)),
24550
25621
  `${source.name} (${source.type})`
24551
25622
  );
24552
- renderRelationships(asArray(source.schema?.relationships));
25623
+ renderRelationships(asArray2(source.schema?.relationships));
24553
25624
  }
24554
25625
  return lines.join("\n");
24555
25626
  }
@@ -24557,8 +25628,8 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24557
25628
  if (typeof sanitized.schema === "string") lines.push(`Schema: ${sanitized.schema}`);
24558
25629
  if (sanitized.description) lines.push(`Description: ${sanitized.description}`);
24559
25630
  lines.push("");
24560
- renderEntities(asArray(sanitized.entities).concat(asArray(sanitized.tables)));
24561
- renderRelationships(asArray(sanitized.relationships));
25631
+ renderEntities(asArray2(sanitized.entities).concat(asArray2(sanitized.tables)));
25632
+ renderRelationships(asArray2(sanitized.relationships));
24562
25633
  return lines.join("\n");
24563
25634
  }
24564
25635
 
@@ -25847,10 +26918,10 @@ function buildDashboardAgentTools(sdk, signal, trace) {
25847
26918
  }
25848
26919
 
25849
26920
  // src/dashboardAgent/collection.ts
25850
- var dynamicImport = new Function("specifier", "return import(specifier)");
26921
+ var dynamicImport2 = new Function("specifier", "return import(specifier)");
25851
26922
  var piModulePromise = null;
25852
26923
  function loadPi() {
25853
- if (!piModulePromise) piModulePromise = dynamicImport("@earendil-works/pi-coding-agent");
26924
+ if (!piModulePromise) piModulePromise = dynamicImport2("@earendil-works/pi-coding-agent");
25854
26925
  return piModulePromise;
25855
26926
  }
25856
26927
  var authAndRegistry = null;
@@ -25968,10 +27039,10 @@ function textDelta(event) {
25968
27039
  return null;
25969
27040
  }
25970
27041
  var runningDashboards = /* @__PURE__ */ new Set();
25971
- function registerDashboardAgentCollection(sdk, config = {}) {
25972
- const piCwd = config.cwd || process.env.PI_AGENT_DIR || import_path8.default.join(process.cwd(), ".prompts", "dashboard-agent");
25973
- const runtimeDir = config.runtimeDir || process.env.PI_AGENT_RUNTIME_DIR || import_path8.default.join(process.cwd(), ".pi-dashboard-agent-runtime");
25974
- const freshSession = config.freshSession ?? process.env.PI_AGENT_FRESH_SESSION !== "false";
27042
+ function registerDashboardAgentCollection(sdk, config2 = {}) {
27043
+ const piCwd = config2.cwd || process.env.PI_AGENT_DIR || import_path8.default.join(process.cwd(), ".prompts", "dashboard-agent");
27044
+ const runtimeDir = config2.runtimeDir || process.env.PI_AGENT_RUNTIME_DIR || import_path8.default.join(process.cwd(), ".pi-dashboard-agent-runtime");
27045
+ const freshSession = config2.freshSession ?? process.env.PI_AGENT_FRESH_SESSION !== "false";
25975
27046
  const SESSIONS_FILE = import_path8.default.join(runtimeDir, ".pi-dashboard-sessions.json");
25976
27047
  const DRAFTS_DIR = import_path8.default.join(runtimeDir, "drafts");
25977
27048
  const DASHBOARD_LOG_FILE = import_path8.default.join(runtimeDir, "dashboard.log");
@@ -26089,8 +27160,8 @@ ${fullPrompt}
26089
27160
  const { createAgentSession, SessionManager } = await loadPi();
26090
27161
  const { authStorage, modelRegistry } = await getAuthAndRegistry();
26091
27162
  let model;
26092
- const explicitProvider = config.provider || process.env.PI_AGENT_PROVIDER;
26093
- const explicitModel = config.model || process.env.PI_AGENT_MODEL;
27163
+ const explicitProvider = config2.provider || process.env.PI_AGENT_PROVIDER;
27164
+ const explicitModel = config2.model || process.env.PI_AGENT_MODEL;
26094
27165
  if (explicitProvider || explicitModel) {
26095
27166
  const providerName = explicitProvider || "openrouter";
26096
27167
  const modelId = explicitModel || "anthropic/claude-sonnet-4.5";
@@ -26458,7 +27529,7 @@ ${description}
26458
27529
  // src/index.ts
26459
27530
  var DEFAULT_WS_URL = "wss://ws.superatom.ai/websocket";
26460
27531
  var SuperatomSDK = class {
26461
- constructor(config) {
27532
+ constructor(config2) {
26462
27533
  this.ws = null;
26463
27534
  this.messageHandlers = /* @__PURE__ */ new Map();
26464
27535
  this.messageTypeHandlers = /* @__PURE__ */ new Map();
@@ -26485,39 +27556,39 @@ var SuperatomSDK = class {
26485
27556
  // since an outage of unknown length shouldn't grow this without limit.
26486
27557
  this.pendingOutbox = [];
26487
27558
  this.MAX_OUTBOX_SIZE = 50;
26488
- if (config.logLevel) {
26489
- logger.setLogLevel(config.logLevel);
26490
- }
26491
- this.apiKey = config.apiKey;
26492
- this.projectId = config.projectId;
26493
- this.type = config.type || "data-agent";
26494
- this.bundleDir = config.bundleDir;
26495
- this.url = config.url || process.env.SA_WEBSOCKET_URL || DEFAULT_WS_URL;
26496
- this.anthropicApiKey = config.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || "";
26497
- this.groqApiKey = config.GROQ_API_KEY || process.env.GROQ_API_KEY || "";
26498
- this.geminiApiKey = config.GEMINI_API_KEY || process.env.GEMINI_API_KEY || "";
26499
- this.openaiApiKey = config.OPENAI_API_KEY || process.env.OPENAI_API_KEY || "";
26500
- this.llmProviders = config.LLM_PROVIDERS || getLLMProviders();
26501
- this.databaseType = config.databaseType || "postgresql";
26502
- setFmtConfig({ locale: config.locale, currency: config.currency });
26503
- this.modelStrategy = config.modelStrategy || "fast";
26504
- this.mainAgentModel = config.mainAgentModel || "anthropic/claude-sonnet-4-5-20250929";
26505
- this.sourceAgentModel = config.sourceAgentModel || "anthropic/claude-sonnet-4-5-20250929";
26506
- this.dashCompModels = config.dashCompModels;
26507
- this.conversationSimilarityThreshold = config.conversationSimilarityThreshold ?? 0.8;
27559
+ if (config2.logLevel) {
27560
+ logger.setLogLevel(config2.logLevel);
27561
+ }
27562
+ this.apiKey = config2.apiKey;
27563
+ this.projectId = config2.projectId;
27564
+ this.type = config2.type || "data-agent";
27565
+ this.bundleDir = config2.bundleDir;
27566
+ this.url = config2.url || process.env.SA_WEBSOCKET_URL || DEFAULT_WS_URL;
27567
+ this.anthropicApiKey = config2.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || "";
27568
+ this.groqApiKey = config2.GROQ_API_KEY || process.env.GROQ_API_KEY || "";
27569
+ this.geminiApiKey = config2.GEMINI_API_KEY || process.env.GEMINI_API_KEY || "";
27570
+ this.openaiApiKey = config2.OPENAI_API_KEY || process.env.OPENAI_API_KEY || "";
27571
+ this.llmProviders = config2.LLM_PROVIDERS || getLLMProviders();
27572
+ this.databaseType = config2.databaseType || "postgresql";
27573
+ setFmtConfig({ locale: config2.locale, currency: config2.currency });
27574
+ this.modelStrategy = config2.modelStrategy || "fast";
27575
+ this.mainAgentModel = config2.mainAgentModel || "anthropic/claude-sonnet-4-5-20250929";
27576
+ this.sourceAgentModel = config2.sourceAgentModel || "anthropic/claude-sonnet-4-5-20250929";
27577
+ this.dashCompModels = config2.dashCompModels;
27578
+ this.conversationSimilarityThreshold = config2.conversationSimilarityThreshold ?? 0.8;
26508
27579
  this.applyModelStrategy(this.modelStrategy);
26509
27580
  this.applyConversationSimilarityThreshold(this.conversationSimilarityThreshold);
26510
- if (config.queryCacheTTL !== void 0) {
26511
- queryCache.setTTL(config.queryCacheTTL);
27581
+ if (config2.queryCacheTTL !== void 0) {
27582
+ queryCache.setTTL(config2.queryCacheTTL);
26512
27583
  }
26513
- if (config.dashboardHistoryTTL !== void 0) {
26514
- dashboardConversationHistory.setTTL(config.dashboardHistoryTTL);
27584
+ if (config2.dashboardHistoryTTL !== void 0) {
27585
+ dashboardConversationHistory.setTTL(config2.dashboardHistoryTTL);
26515
27586
  }
26516
27587
  logger.info(`Initializing Superatom SDK for project ${this.projectId}, llm providers: ${this.llmProviders.join(", ")}, database type: ${this.databaseType}, model strategy: ${this.modelStrategy}, query cache TTL: ${queryCache.getTTL()} minutes`);
26517
27588
  this.userManager = new UserManager(this.projectId, 5e3);
26518
27589
  this.dashboardManager = new DashboardManager(this.projectId);
26519
27590
  this.reportManager = new ReportManager(this.projectId);
26520
- this.initializePromptLoader(config.promptsDir).catch((error) => {
27591
+ this.initializePromptLoader(config2.promptsDir).catch((error) => {
26521
27592
  logger.error("Failed to initialize PromptLoader:", error);
26522
27593
  });
26523
27594
  this.initializeUserManager().catch((error) => {
@@ -26746,6 +27817,14 @@ var SuperatomSDK = class {
26746
27817
  case "DASHBOARD_AGENT_ABORT_REQ":
26747
27818
  handleDashboardAgentAbort(parsed, (msg) => this.send(msg));
26748
27819
  break;
27820
+ case "CODING_AGENT_REQ":
27821
+ handleCodingAgentRequest(parsed, this.collections, (msg) => this.send(msg)).catch((error) => {
27822
+ logger.error("Failed to handle coding agent request:", error);
27823
+ });
27824
+ break;
27825
+ case "CODING_AGENT_ABORT_REQ":
27826
+ handleCodingAgentAbort(parsed, (msg) => this.send(msg));
27827
+ break;
26749
27828
  case "REPORT_COMP_REQ":
26750
27829
  handleReportCompRequest(parsed, this.components, (msg) => this.send(msg), this.anthropicApiKey, this.groqApiKey, this.geminiApiKey, this.openaiApiKey, this.llmProviders, this.collections, this.tools, this.dashCompModels).catch((error) => {
26751
27830
  logger.error("Failed to handle report comp request:", error);
@@ -27084,20 +28163,35 @@ var SuperatomSDK = class {
27084
28163
  UILogCollector,
27085
28164
  UserManager,
27086
28165
  anthropicLLM,
28166
+ assertReadOnlySql,
28167
+ createCodexSession,
28168
+ createCodingAgentSession,
27087
28169
  dashboardConversationHistory,
28170
+ describeTriggers,
28171
+ emitAgentEvent,
27088
28172
  geminiLLM,
28173
+ getCodingAgent,
28174
+ getCodingAgentsConfig,
27089
28175
  groqLLM,
27090
28176
  hybridRerank,
28177
+ listCodingAgents,
27091
28178
  llmUsageLogger,
27092
28179
  logger,
27093
28180
  normalizeScriptBody,
28181
+ onAgentRun,
27094
28182
  openaiLLM,
27095
28183
  queryCache,
28184
+ registerCodingAgentCollection,
28185
+ registerCodingAgents,
27096
28186
  registerDashboardAgentCollection,
27097
28187
  rerankChromaResults,
27098
28188
  rerankConversationResults,
27099
28189
  resolveScriptRecipeStore,
28190
+ runCodingAgent,
27100
28191
  runScript,
27101
- userPromptErrorLogger
28192
+ startAgentTriggers,
28193
+ stopAgentTriggers,
28194
+ userPromptErrorLogger,
28195
+ writeBridgeShims
27102
28196
  });
27103
28197
  //# sourceMappingURL=index.js.map