@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.mjs CHANGED
@@ -782,6 +782,37 @@ var DashboardAgentAbortRequestMessageSchema = z3.object({
782
782
  type: z3.literal("DASHBOARD_AGENT_ABORT_REQ"),
783
783
  payload: DashboardAgentAbortRequestPayloadSchema
784
784
  });
785
+ var CodingAgentRequestPayloadSchema = z3.object({
786
+ prompt: z3.string(),
787
+ /** The chat thread. One agent session per thread, and the abort key. */
788
+ threadId: z3.string(),
789
+ projectId: z3.string().optional(),
790
+ userId: z3.string().optional(),
791
+ /**
792
+ * Which registered agent to run (see registerCodingAgents). Omit for the
793
+ * deployment's plain chat agent — the pre-registry behaviour.
794
+ */
795
+ agentId: z3.string().optional(),
796
+ /** Which harness to run. Defaults to whatever the deployment registered. */
797
+ harness: z3.string().optional(),
798
+ /** Continue a prior agent session — the sessionId a previous run returned. */
799
+ resumeId: z3.string().optional()
800
+ });
801
+ var CodingAgentRequestMessageSchema = z3.object({
802
+ id: z3.string(),
803
+ from: MessageParticipantSchema,
804
+ type: z3.literal("CODING_AGENT_REQ"),
805
+ payload: CodingAgentRequestPayloadSchema
806
+ });
807
+ var CodingAgentAbortRequestPayloadSchema = z3.object({
808
+ threadId: z3.string()
809
+ });
810
+ var CodingAgentAbortRequestMessageSchema = z3.object({
811
+ id: z3.string().optional(),
812
+ from: MessageParticipantSchema,
813
+ type: z3.literal("CODING_AGENT_ABORT_REQ"),
814
+ payload: CodingAgentAbortRequestPayloadSchema
815
+ });
785
816
  var ReportCompRequestPayloadSchema = z3.object({
786
817
  prompt: z3.string(),
787
818
  userId: z3.string().optional(),
@@ -9052,12 +9083,12 @@ answer.
9052
9083
 
9053
9084
  // src/userResponse/prompt-loader.ts
9054
9085
  var PromptLoader = class {
9055
- constructor(config) {
9086
+ constructor(config2) {
9056
9087
  this.promptCache = /* @__PURE__ */ new Map();
9057
9088
  this.databaseRulesCache = /* @__PURE__ */ new Map();
9058
9089
  this.isInitialized = false;
9059
9090
  this.databaseType = "postgresql";
9060
- this.promptsDir = config?.promptsDir || path4.join(process.cwd(), ".prompts");
9091
+ this.promptsDir = config2?.promptsDir || path4.join(process.cwd(), ".prompts");
9061
9092
  }
9062
9093
  /**
9063
9094
  * Load a prompt template from file system OR fallback to hardcoded prompts
@@ -9973,7 +10004,7 @@ function validateAndFixSqlQuery(query, dbType) {
9973
10004
 
9974
10005
  // src/userResponse/agents/source-agent.ts
9975
10006
  var SourceAgent = class {
9976
- constructor(tool, config, streamBuffer, options) {
10007
+ constructor(tool, config2, streamBuffer, options) {
9977
10008
  this.attempts = 0;
9978
10009
  /**
9979
10010
  * Reference to this dispatch's own entry in trace.schemaNodes.sourceAgentSchemas
@@ -9983,7 +10014,7 @@ var SourceAgent = class {
9983
10014
  */
9984
10015
  this.traceSchemaEntry = null;
9985
10016
  this.tool = tool;
9986
- this.config = config;
10017
+ this.config = config2;
9987
10018
  this.streamBuffer = streamBuffer;
9988
10019
  this.options = options || {};
9989
10020
  }
@@ -11583,7 +11614,7 @@ var WRITE_ANALYSIS_TOOL_DEF = {
11583
11614
  var MAX_SCRIPT_ATTEMPTS = 3;
11584
11615
  var MAX_ANALYSIS_ATTEMPTS = 3;
11585
11616
  var MainAgent = class {
11586
- constructor(externalTools, config, scriptStore, turnId, streamBuffer, workflows = [], forkMode = false, editContext, componentCatalog = [], displayPreference) {
11617
+ constructor(externalTools, config2, scriptStore, turnId, streamBuffer, workflows = [], forkMode = false, editContext, componentCatalog = [], displayPreference) {
11587
11618
  this.createdFromPrompt = "";
11588
11619
  this.scriptState = {
11589
11620
  recipeId: null,
@@ -11629,7 +11660,7 @@ var MainAgent = class {
11629
11660
  this.displayPreference = null;
11630
11661
  this.externalTools = externalTools;
11631
11662
  this.workflows = workflows;
11632
- this.config = config;
11663
+ this.config = config2;
11633
11664
  this.streamBuffer = streamBuffer || new StreamBuffer();
11634
11665
  this.scriptStore = scriptStore ?? null;
11635
11666
  this.turnId = turnId ?? null;
@@ -13298,7 +13329,7 @@ function validateExternalTool(externalTool, executedTools, providerName) {
13298
13329
  const executedTool = executedTools?.find((t) => t.id === toolId);
13299
13330
  return { valid: true, executedTool };
13300
13331
  }
13301
- function validateAndCleanQuery(query, config) {
13332
+ function validateAndCleanQuery(query, config2) {
13302
13333
  if (!query) {
13303
13334
  return { query: null, wasModified: false };
13304
13335
  }
@@ -13306,12 +13337,12 @@ function validateAndCleanQuery(query, config) {
13306
13337
  let cleanedQuery = query;
13307
13338
  const queryStr = typeof query === "string" ? query : query?.sql || "";
13308
13339
  if (queryStr.includes("OPENJSON") || queryStr.includes("JSON_VALUE")) {
13309
- logger.warn(`[${config.providerName}] Query contains OPENJSON/JSON_VALUE (invalid - cannot parse tool result), setting query to null`);
13340
+ logger.warn(`[${config2.providerName}] Query contains OPENJSON/JSON_VALUE (invalid - cannot parse tool result), setting query to null`);
13310
13341
  return { query: null, wasModified: true };
13311
13342
  }
13312
13343
  const { query: fixedQuery, fixed, fixes } = validateAndFixSqlQuery(queryStr);
13313
13344
  if (fixed) {
13314
- logger.warn(`[${config.providerName}] SQL fixes applied to component query: ${fixes.join("; ")}`);
13345
+ logger.warn(`[${config2.providerName}] SQL fixes applied to component query: ${fixes.join("; ")}`);
13315
13346
  wasModified = true;
13316
13347
  if (typeof cleanedQuery === "string") {
13317
13348
  cleanedQuery = fixedQuery;
@@ -13319,36 +13350,36 @@ function validateAndCleanQuery(query, config) {
13319
13350
  cleanedQuery = { ...cleanedQuery, sql: fixedQuery };
13320
13351
  }
13321
13352
  }
13322
- const maxLimit = config.maxLimit ?? MAX_AGENT_QUERY_LIMIT;
13353
+ const maxLimit = config2.maxLimit ?? MAX_AGENT_QUERY_LIMIT;
13323
13354
  if (typeof cleanedQuery === "string") {
13324
- const limitedQuery = ensureQueryLimit(cleanedQuery, config.defaultLimit, maxLimit);
13355
+ const limitedQuery = ensureQueryLimit(cleanedQuery, config2.defaultLimit, maxLimit);
13325
13356
  if (limitedQuery !== cleanedQuery) wasModified = true;
13326
13357
  cleanedQuery = limitedQuery;
13327
13358
  } else if (cleanedQuery?.sql) {
13328
- const limitedSql = ensureQueryLimit(cleanedQuery.sql, config.defaultLimit, maxLimit);
13359
+ const limitedSql = ensureQueryLimit(cleanedQuery.sql, config2.defaultLimit, maxLimit);
13329
13360
  if (limitedSql !== cleanedQuery.sql) wasModified = true;
13330
13361
  cleanedQuery = { ...cleanedQuery, sql: limitedSql };
13331
13362
  }
13332
13363
  return { query: cleanedQuery, wasModified };
13333
13364
  }
13334
- function processComponentProps(props, executedTools, config) {
13365
+ function processComponentProps(props, executedTools, config2) {
13335
13366
  let cleanedProps = { ...props };
13336
13367
  if (cleanedProps.externalTool) {
13337
13368
  const { valid } = validateExternalTool(
13338
13369
  cleanedProps.externalTool,
13339
13370
  executedTools,
13340
- config.providerName
13371
+ config2.providerName
13341
13372
  );
13342
13373
  if (!valid) {
13343
13374
  cleanedProps.externalTool = null;
13344
13375
  }
13345
13376
  }
13346
13377
  if (cleanedProps.query) {
13347
- const { query } = validateAndCleanQuery(cleanedProps.query, config);
13378
+ const { query } = validateAndCleanQuery(cleanedProps.query, config2);
13348
13379
  cleanedProps.query = query;
13349
13380
  }
13350
13381
  if (cleanedProps.query && cleanedProps.externalTool) {
13351
- logger.info(`[${config.providerName}] Both query and externalTool exist, keeping both - frontend will decide`);
13382
+ logger.info(`[${config2.providerName}] Both query and externalTool exist, keeping both - frontend will decide`);
13352
13383
  }
13353
13384
  return cleanedProps;
13354
13385
  }
@@ -13921,15 +13952,15 @@ ${carried.trim()}
13921
13952
  }
13922
13953
  /** slug of name, with a short id suffix when the bare slug is already taken. */
13923
13954
  async computeFileBase(name, id) {
13924
- const slug = this.toSlug(name);
13955
+ const slug2 = this.toSlug(name);
13925
13956
  if (this.store) {
13926
13957
  try {
13927
- const taken = await this.store.fileBaseTaken(slug, id, this.projectId);
13928
- if (taken) return `${slug}-${id.slice(-6)}`;
13958
+ const taken = await this.store.fileBaseTaken(slug2, id, this.projectId);
13959
+ if (taken) return `${slug2}-${id.slice(-6)}`;
13929
13960
  } catch {
13930
13961
  }
13931
13962
  }
13932
- return slug;
13963
+ return slug2;
13933
13964
  }
13934
13965
  toSlug(name) {
13935
13966
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unnamed-script";
@@ -15318,8 +15349,8 @@ var schema = new Schema();
15318
15349
 
15319
15350
  // src/userResponse/services/query-execution-service.ts
15320
15351
  var QueryExecutionService = class {
15321
- constructor(config) {
15322
- this.config = config;
15352
+ constructor(config2) {
15353
+ this.config = config2;
15323
15354
  }
15324
15355
  /**
15325
15356
  * Get the cache key for a query
@@ -15552,12 +15583,12 @@ Fixed SQL query:`;
15552
15583
 
15553
15584
  // src/userResponse/services/tool-executor-service.ts
15554
15585
  var ToolExecutorService = class {
15555
- constructor(config) {
15586
+ constructor(config2) {
15556
15587
  this.queryAttempts = /* @__PURE__ */ new Map();
15557
15588
  this.toolAttempts = /* @__PURE__ */ new Map();
15558
15589
  this.executedToolsList = [];
15559
15590
  this.maxAttemptsReached = false;
15560
- this.config = config;
15591
+ this.config = config2;
15561
15592
  }
15562
15593
  /**
15563
15594
  * Reset state for a new execution
@@ -15869,13 +15900,13 @@ ${errorMsg}
15869
15900
 
15870
15901
  // src/userResponse/base-llm.ts
15871
15902
  var BaseLLM = class {
15872
- constructor(config) {
15873
- this.model = config?.model || this.getDefaultModel();
15874
- this.fastModel = config?.fastModel || this.getDefaultFastModel();
15875
- this.defaultLimit = config?.defaultLimit || 10;
15876
- this.apiKey = config?.apiKey;
15877
- this.modelStrategy = config?.modelStrategy || "fast";
15878
- this.conversationSimilarityThreshold = config?.conversationSimilarityThreshold || DEFAULT_CONVERSATION_SIMILARITY_THRESHOLD;
15903
+ constructor(config2) {
15904
+ this.model = config2?.model || this.getDefaultModel();
15905
+ this.fastModel = config2?.fastModel || this.getDefaultFastModel();
15906
+ this.defaultLimit = config2?.defaultLimit || 10;
15907
+ this.apiKey = config2?.apiKey;
15908
+ this.modelStrategy = config2?.modelStrategy || "fast";
15909
+ this.conversationSimilarityThreshold = config2?.conversationSimilarityThreshold || DEFAULT_CONVERSATION_SIMILARITY_THRESHOLD;
15879
15910
  this.queryService = new QueryExecutionService({
15880
15911
  defaultLimit: this.defaultLimit,
15881
15912
  getModelForTask: (taskType) => this.getModelForTask(taskType),
@@ -17064,8 +17095,8 @@ ${executedToolsText}`);
17064
17095
  // src/userResponse/anthropic.ts
17065
17096
  dotenv.config({ quiet: true });
17066
17097
  var AnthropicLLM = class extends BaseLLM {
17067
- constructor(config) {
17068
- super(config);
17098
+ constructor(config2) {
17099
+ super(config2);
17069
17100
  }
17070
17101
  getDefaultModel() {
17071
17102
  return "anthropic/claude-sonnet-4-5-20250929";
@@ -17086,8 +17117,8 @@ var anthropicLLM = new AnthropicLLM();
17086
17117
  import dotenv2 from "dotenv";
17087
17118
  dotenv2.config({ quiet: true });
17088
17119
  var GroqLLM = class extends BaseLLM {
17089
- constructor(config) {
17090
- super(config);
17120
+ constructor(config2) {
17121
+ super(config2);
17091
17122
  }
17092
17123
  getDefaultModel() {
17093
17124
  return "groq/openai/gpt-oss-120b";
@@ -17108,8 +17139,8 @@ var groqLLM = new GroqLLM();
17108
17139
  import dotenv3 from "dotenv";
17109
17140
  dotenv3.config({ quiet: true });
17110
17141
  var GeminiLLM = class extends BaseLLM {
17111
- constructor(config) {
17112
- super(config);
17142
+ constructor(config2) {
17143
+ super(config2);
17113
17144
  }
17114
17145
  getDefaultModel() {
17115
17146
  return "gemini/gemini-2.5-flash";
@@ -17130,8 +17161,8 @@ var geminiLLM = new GeminiLLM();
17130
17161
  import dotenv4 from "dotenv";
17131
17162
  dotenv4.config({ quiet: true });
17132
17163
  var OpenAILLM = class extends BaseLLM {
17133
- constructor(config) {
17134
- super(config);
17164
+ constructor(config2) {
17165
+ super(config2);
17135
17166
  }
17136
17167
  getDefaultModel() {
17137
17168
  return "openai/gpt-4.1";
@@ -23599,6 +23630,1031 @@ async function handleDashboardAgentRequest(data, collections, sendMessage) {
23599
23630
  }
23600
23631
  }
23601
23632
 
23633
+ // src/codingAgent/drivers/codex.ts
23634
+ var dynamicImport = new Function("specifier", "return import(specifier)");
23635
+ var codexModulePromise = null;
23636
+ function loadCodex() {
23637
+ if (!codexModulePromise) {
23638
+ codexModulePromise = dynamicImport("@openai/codex-sdk").catch((err) => {
23639
+ codexModulePromise = null;
23640
+ throw new Error(
23641
+ `@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})`
23642
+ );
23643
+ });
23644
+ }
23645
+ return codexModulePromise;
23646
+ }
23647
+ var ENV_ALLOWLIST = ["PATH", "HOME", "USER", "LOGNAME", "SHELL", "LANG", "LC_ALL", "TMPDIR", "TERM", "NODE_EXTRA_CA_CERTS"];
23648
+ var ENV_PREFIX_ALLOWLIST = ["CODEX_", "OPENAI_"];
23649
+ var DONE_POLL_MS = 250;
23650
+ var USAGE_GRACE_MS = 4e3;
23651
+ var BUFFER_CAP = 64e3;
23652
+ var EVENT_CAP = 600;
23653
+ function emptyUsage() {
23654
+ return { inputTokens: 0, outputTokens: 0, reasoningTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, toolCalls: 0 };
23655
+ }
23656
+ function addUsage(acc, u) {
23657
+ acc.inputTokens += u.input_tokens ?? 0;
23658
+ acc.outputTokens += u.output_tokens ?? 0;
23659
+ acc.reasoningTokens += u.reasoning_output_tokens ?? 0;
23660
+ acc.cacheReadTokens += u.cached_input_tokens ?? 0;
23661
+ acc.cacheWriteTokens += u.cache_write_input_tokens ?? 0;
23662
+ }
23663
+ function normEvent(ev, done) {
23664
+ const it = ev.item;
23665
+ if (!it) return null;
23666
+ const status = it.status;
23667
+ switch (it.type) {
23668
+ case "agent_message":
23669
+ return { kind: "message", id: it.id, text: it.text ?? "", done };
23670
+ case "reasoning":
23671
+ return { kind: "reasoning", id: it.id, text: it.text ?? "", done };
23672
+ case "command_execution":
23673
+ return { kind: "command", id: it.id, command: it.command ?? "", output: it.aggregated_output ?? "", status, done };
23674
+ case "mcp_tool_call":
23675
+ return { kind: "tool", id: it.id, tool: { server: it.server, name: it.tool ?? "tool", args: it.arguments }, status, done };
23676
+ case "web_search":
23677
+ return { kind: "tool", id: it.id, tool: { name: "web_search", args: { query: it.query } }, status, done };
23678
+ case "file_change":
23679
+ return { kind: "file", id: it.id, text: (it.changes ?? []).map((c) => `${c.kind} ${c.path}`).join(", "), status, done };
23680
+ case "todo_list":
23681
+ return { kind: "todo", id: it.id, text: (it.items ?? []).map((t) => `${t.completed ? "[x]" : "[ ]"} ${t.text}`).join("\n"), done };
23682
+ case "error":
23683
+ return { kind: "error", id: it.id, text: it.message ?? "", done };
23684
+ default:
23685
+ return null;
23686
+ }
23687
+ }
23688
+ function markerFor(event) {
23689
+ switch (event.kind) {
23690
+ case "command":
23691
+ return event.command ? `\u2192 ${event.command.replace(/\s+/g, " ").slice(0, 160)}` : null;
23692
+ case "tool": {
23693
+ const args = event.tool?.args;
23694
+ const detail = args && typeof args === "object" ? String(args.sql ?? args.query ?? args.command ?? "").replace(/\s+/g, " ").slice(0, 140) : "";
23695
+ return `\u2192 ${event.tool?.name ?? "tool"}${detail ? " " + detail : ""}`;
23696
+ }
23697
+ case "file":
23698
+ return event.text ? `\u270E ${event.text}` : null;
23699
+ default:
23700
+ return null;
23701
+ }
23702
+ }
23703
+ function buildEnv(opts) {
23704
+ if (opts.inheritEnv) return opts.env ? { ...process.env, ...opts.env } : void 0;
23705
+ const env = {};
23706
+ for (const [k, v] of Object.entries(process.env)) {
23707
+ if (v === void 0) continue;
23708
+ if (ENV_ALLOWLIST.includes(k) || ENV_PREFIX_ALLOWLIST.some((p) => k.startsWith(p))) env[k] = v;
23709
+ }
23710
+ return { ...env, ...opts.env ?? {} };
23711
+ }
23712
+ function createCodexSession(opts) {
23713
+ const model = opts.model ?? process.env.CODEX_AGENT_MODEL;
23714
+ let thread = null;
23715
+ let resumeId = opts.resumeId;
23716
+ let threadId = opts.resumeId;
23717
+ let resumeFailed = false;
23718
+ let running = false;
23719
+ let buf = "";
23720
+ const eventLog = [];
23721
+ const threadOptions = () => ({
23722
+ ...model ? { model } : {},
23723
+ workingDirectory: opts.cwd,
23724
+ sandboxMode: opts.sandboxMode ?? "workspace-write",
23725
+ // Headless: a prompt for approval would hang forever with nobody to answer it.
23726
+ approvalPolicy: opts.approvalPolicy ?? "never",
23727
+ networkAccessEnabled: opts.networkAccessEnabled ?? false,
23728
+ skipGitRepoCheck: true,
23729
+ ...opts.reasoningEffort ? { modelReasoningEffort: opts.reasoningEffort } : {}
23730
+ });
23731
+ async function ensureThread() {
23732
+ if (thread) return thread;
23733
+ const { Codex } = await loadCodex();
23734
+ const codex = new Codex({
23735
+ ...opts.apiKey ? { apiKey: opts.apiKey } : {},
23736
+ ...opts.baseUrl ? { baseUrl: opts.baseUrl } : {},
23737
+ env: buildEnv(opts),
23738
+ // Tools reach codex as MCP servers; each server's name shows up on
23739
+ // every tool event, so the runner can tell ours from codex's own.
23740
+ // Only send a config override when there is something in it. An empty
23741
+ // object is truthy, so the naive check sent config:{mcp_servers:{}} on
23742
+ // every run — a pointless CLI override on a path that is otherwise
23743
+ // completely default.
23744
+ ...opts.mcpServers && Object.keys(opts.mcpServers).length || opts.extraConfig ? { config: { ...opts.mcpServers && Object.keys(opts.mcpServers).length ? { mcp_servers: opts.mcpServers } : {}, ...opts.extraConfig ?? {} } } : {}
23745
+ });
23746
+ thread = resumeId ? codex.resumeThread(resumeId, threadOptions()) : codex.startThread(threadOptions());
23747
+ return thread;
23748
+ }
23749
+ async function runTurn(prompt, handlers, signal) {
23750
+ const startedAt = Date.now();
23751
+ const usage = emptyUsage();
23752
+ let answer = "";
23753
+ let error;
23754
+ let status = "completed";
23755
+ const finish = () => ({ status, answer, ms: Date.now() - startedAt, usage, error, sessionId: threadId });
23756
+ if (signal?.aborted) {
23757
+ status = "aborted";
23758
+ return finish();
23759
+ }
23760
+ const controller = new AbortController();
23761
+ let earlyExit = false;
23762
+ const onCallerAbort = () => controller.abort();
23763
+ signal?.addEventListener("abort", onCallerAbort, { once: true });
23764
+ let poll = null;
23765
+ let graceTimer = null;
23766
+ if (handlers?.doneWhen) {
23767
+ poll = setInterval(() => {
23768
+ void (async () => {
23769
+ try {
23770
+ if (!await handlers.doneWhen() || earlyExit) return;
23771
+ earlyExit = true;
23772
+ if (poll) {
23773
+ clearInterval(poll);
23774
+ poll = null;
23775
+ }
23776
+ graceTimer = setTimeout(() => controller.abort(), USAGE_GRACE_MS);
23777
+ } catch {
23778
+ }
23779
+ })();
23780
+ }, DONE_POLL_MS);
23781
+ }
23782
+ const emitted = /* @__PURE__ */ new Map();
23783
+ const emit = (chunk, narrate) => {
23784
+ buf = (buf + chunk).slice(-BUFFER_CAP);
23785
+ handlers?.onOutput?.(chunk);
23786
+ if (narrate) handlers?.onNarration?.(chunk);
23787
+ };
23788
+ try {
23789
+ const t = await ensureThread();
23790
+ const streamed = await t.runStreamed(prompt, { signal: controller.signal });
23791
+ for await (const ev of streamed.events) {
23792
+ if (ev.type === "thread.started" && ev.thread_id) threadId = ev.thread_id;
23793
+ if (ev.type === "turn.completed" && ev.usage) {
23794
+ addUsage(usage, ev.usage);
23795
+ handlers?.onUsage?.({ ...usage });
23796
+ if (earlyExit) {
23797
+ if (graceTimer) {
23798
+ clearTimeout(graceTimer);
23799
+ graceTimer = null;
23800
+ }
23801
+ controller.abort();
23802
+ }
23803
+ continue;
23804
+ }
23805
+ if (ev.type === "turn.failed") {
23806
+ error = ev.error?.message ?? "codex turn failed";
23807
+ status = "error";
23808
+ continue;
23809
+ }
23810
+ if (ev.type === "error") {
23811
+ error = ev.message ?? "codex stream error";
23812
+ status = "error";
23813
+ continue;
23814
+ }
23815
+ if (ev.type === "turn.started") {
23816
+ const e = { kind: "turn" };
23817
+ eventLog.push(e);
23818
+ handlers?.onEvent?.(e);
23819
+ continue;
23820
+ }
23821
+ if (ev.type !== "item.started" && ev.type !== "item.updated" && ev.type !== "item.completed") continue;
23822
+ const done = ev.type === "item.completed";
23823
+ const event = normEvent(ev, done);
23824
+ if (!event) continue;
23825
+ eventLog.push(event);
23826
+ if (eventLog.length > EVENT_CAP) eventLog.shift();
23827
+ handlers?.onEvent?.(event);
23828
+ if (done && (event.kind === "tool" || event.kind === "command")) usage.toolCalls++;
23829
+ if (event.kind === "message") {
23830
+ const text = event.text ?? "";
23831
+ const prev = emitted.get(event.id ?? "") ?? 0;
23832
+ if (text.length > prev) {
23833
+ emitted.set(event.id ?? "", text.length);
23834
+ emit(text.slice(prev), true);
23835
+ }
23836
+ if (done && text) answer = text;
23837
+ continue;
23838
+ }
23839
+ const key = `m:${event.id}`;
23840
+ if (!emitted.has(key)) {
23841
+ const marker = markerFor(event);
23842
+ if (marker) {
23843
+ emitted.set(key, 1);
23844
+ emit(`
23845
+ ${marker}
23846
+ `, false);
23847
+ }
23848
+ }
23849
+ }
23850
+ if (earlyExit) status = "early";
23851
+ } catch (err) {
23852
+ if (earlyExit) status = "early";
23853
+ else if (signal?.aborted) status = "aborted";
23854
+ else if (resumeId && !resumeFailed) {
23855
+ resumeFailed = true;
23856
+ resumeId = void 0;
23857
+ thread = null;
23858
+ threadId = void 0;
23859
+ signal?.removeEventListener("abort", onCallerAbort);
23860
+ if (poll) clearInterval(poll);
23861
+ return runTurn(prompt, handlers, signal);
23862
+ } else {
23863
+ status = "error";
23864
+ error = err?.message ?? String(err);
23865
+ }
23866
+ } finally {
23867
+ if (poll) clearInterval(poll);
23868
+ if (graceTimer) clearTimeout(graceTimer);
23869
+ signal?.removeEventListener("abort", onCallerAbort);
23870
+ }
23871
+ return finish();
23872
+ }
23873
+ return {
23874
+ harness: "codex",
23875
+ kind: "events",
23876
+ async run(prompt, handlers, signal) {
23877
+ if (running) throw new Error("This codex session already has a turn in flight.");
23878
+ running = true;
23879
+ try {
23880
+ return await runTurn(prompt, handlers, signal);
23881
+ } finally {
23882
+ running = false;
23883
+ }
23884
+ },
23885
+ events: () => eventLog,
23886
+ buffer: () => buf,
23887
+ busy: () => running,
23888
+ sessionId: () => threadId,
23889
+ reset: () => {
23890
+ thread = null;
23891
+ resumeId = void 0;
23892
+ threadId = void 0;
23893
+ resumeFailed = false;
23894
+ buf = "";
23895
+ eventLog.length = 0;
23896
+ },
23897
+ dispose: () => {
23898
+ thread = null;
23899
+ }
23900
+ };
23901
+ }
23902
+
23903
+ // src/codingAgent/factory.ts
23904
+ function createCodingAgentSession(harness, opts) {
23905
+ switch (harness) {
23906
+ case "codex":
23907
+ return createCodexSession(opts);
23908
+ default:
23909
+ throw new Error(`Unknown coding-agent harness: ${harness}`);
23910
+ }
23911
+ }
23912
+
23913
+ // src/codingAgent/registry.ts
23914
+ import * as path8 from "path";
23915
+ var agents = /* @__PURE__ */ new Map();
23916
+ var config = {
23917
+ promptRoot: path8.join(process.cwd(), ".prompts", "coding-agents"),
23918
+ runtimeDir: path8.join(process.cwd(), ".coding-agent-runtime"),
23919
+ harness: "codex",
23920
+ keepRuns: 20
23921
+ };
23922
+ function registerCodingAgents(definitions, options = {}) {
23923
+ config = {
23924
+ ...config,
23925
+ ...options,
23926
+ promptRoot: options.promptRoot || process.env.CODING_AGENT_PROMPT_DIR || config.promptRoot,
23927
+ runtimeDir: options.runtimeDir || process.env.CODING_AGENT_RUNTIME_DIR || config.runtimeDir,
23928
+ harness: options.harness || process.env.CODING_AGENT_HARNESS || config.harness,
23929
+ keepRuns: options.keepRuns ?? config.keepRuns
23930
+ };
23931
+ for (const def of definitions) {
23932
+ if (!def.id) throw new Error("A coding agent definition must have an id.");
23933
+ if (def.mode === "task" && !def.output) {
23934
+ throw new Error(`Coding agent "${def.id}" is mode 'task' but declares no output contract.`);
23935
+ }
23936
+ agents.set(def.id, def);
23937
+ }
23938
+ console.log(`\u2713 coding agents registered: ${[...agents.keys()].join(", ") || "(none)"}`);
23939
+ }
23940
+ function getCodingAgent(id) {
23941
+ return agents.get(id);
23942
+ }
23943
+ function listCodingAgents() {
23944
+ return [...agents.values()].map((a) => ({
23945
+ id: a.id,
23946
+ title: a.title,
23947
+ description: a.description,
23948
+ mode: a.mode
23949
+ }));
23950
+ }
23951
+ function getCodingAgentsConfig() {
23952
+ return config;
23953
+ }
23954
+ function resolvePromptDir(def) {
23955
+ return def.promptDir || path8.join(config.promptRoot, def.id);
23956
+ }
23957
+
23958
+ // src/codingAgent/runner.ts
23959
+ import * as fs8 from "fs";
23960
+ import * as fsp from "fs/promises";
23961
+ import * as path9 from "path";
23962
+ var DEFAULT_TIMEOUT_MS = 15 * 60 * 1e3;
23963
+ var inFlight = /* @__PURE__ */ new Set();
23964
+ function slug(v) {
23965
+ return v.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80) || "unknown";
23966
+ }
23967
+ function resolveScope(def, opts) {
23968
+ const scope = def.scope ?? (def.mode === "chat" ? "thread" : "project");
23969
+ if (scope === "thread") {
23970
+ const id = slug(opts.threadId || "default");
23971
+ return { key: `thread:${id}`, dirName: path9.join("threads", id) };
23972
+ }
23973
+ if (scope === "user") {
23974
+ const id = slug(opts.userId || "anonymous");
23975
+ return { key: `user:${id}`, dirName: path9.join("users", id) };
23976
+ }
23977
+ return { key: "project", dirName: "_project" };
23978
+ }
23979
+ function makeRunId() {
23980
+ const t = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+/, "");
23981
+ return `${t}-${Math.random().toString(36).slice(2, 8)}`;
23982
+ }
23983
+ function statePath(scopeDir) {
23984
+ return path9.join(scopeDir, "_state.json");
23985
+ }
23986
+ function loadState(scopeDir) {
23987
+ try {
23988
+ return JSON.parse(fs8.readFileSync(statePath(scopeDir), "utf-8"));
23989
+ } catch {
23990
+ return { turns: 0, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
23991
+ }
23992
+ }
23993
+ function saveState(scopeDir, state) {
23994
+ try {
23995
+ fs8.mkdirSync(scopeDir, { recursive: true });
23996
+ fs8.writeFileSync(statePath(scopeDir), JSON.stringify({ ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
23997
+ } catch (err) {
23998
+ logger.warn(`[CodingAgent] failed to persist session state: ${err?.message ?? err}`);
23999
+ }
24000
+ }
24001
+ async function clearTransient(dir) {
24002
+ await fsp.rm(path9.join(dir, "input"), { recursive: true, force: true });
24003
+ }
24004
+ function createWorkspace(dir, runId) {
24005
+ const abs = (rel) => path9.join(dir, rel);
24006
+ const ensureDir = async (rel) => {
24007
+ await fsp.mkdir(path9.dirname(abs(rel)), { recursive: true });
24008
+ };
24009
+ return {
24010
+ dir,
24011
+ runId,
24012
+ pathTo: abs,
24013
+ async write(rel, contents) {
24014
+ await ensureDir(rel);
24015
+ await fsp.writeFile(abs(rel), contents, "utf-8");
24016
+ },
24017
+ async writeJson(rel, value) {
24018
+ await ensureDir(rel);
24019
+ await fsp.writeFile(abs(rel), JSON.stringify(value, null, 2), "utf-8");
24020
+ },
24021
+ async writeJsonl(rel, rows) {
24022
+ await ensureDir(rel);
24023
+ await fsp.writeFile(abs(rel), rows.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf-8");
24024
+ },
24025
+ async read(rel) {
24026
+ try {
24027
+ return await fsp.readFile(abs(rel), "utf-8");
24028
+ } catch {
24029
+ return null;
24030
+ }
24031
+ }
24032
+ };
24033
+ }
24034
+ async function installInstructions(def, ws) {
24035
+ const src = path9.join(resolvePromptDir(def), "system.md");
24036
+ try {
24037
+ await ws.write("AGENTS.md", await fsp.readFile(src, "utf-8"));
24038
+ } catch {
24039
+ logger.warn(`[CodingAgent:${def.id}] no system.md at ${src} \u2014 running without standing instructions`);
24040
+ }
24041
+ }
24042
+ async function pruneRuns(agentDir, keep) {
24043
+ if (keep <= 0) return;
24044
+ try {
24045
+ const entries = (await fsp.readdir(agentDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
24046
+ for (const name of entries.slice(0, Math.max(0, entries.length - keep))) {
24047
+ await fsp.rm(path9.join(agentDir, name), { recursive: true, force: true });
24048
+ }
24049
+ } catch {
24050
+ }
24051
+ }
24052
+ async function runCodingAgent(agentId, options = {}) {
24053
+ const def = getCodingAgent(agentId);
24054
+ if (!def) {
24055
+ const known = listCodingAgents().map((a) => a.id);
24056
+ throw new Error(
24057
+ `Unknown coding agent "${agentId}". Registered: ${known.join(", ") || "(none \u2014 was registerCodingAgents called?)"}`
24058
+ );
24059
+ }
24060
+ const cfg = getCodingAgentsConfig();
24061
+ const runId = makeRunId();
24062
+ const agentDir = path9.join(cfg.runtimeDir, def.id);
24063
+ const policy = def.session ?? {};
24064
+ const persistentWorkspace = policy.workspace === "persistent";
24065
+ const persistentSession = policy.scope === "persistent";
24066
+ const { key: scopeKey, dirName } = resolveScope(def, { userId: options.userId, threadId: options.threadId });
24067
+ const scopeDir = path9.join(agentDir, dirName);
24068
+ const dir = options.workspaceDir || (persistentWorkspace ? scopeDir : path9.join(scopeDir, "runs", runId));
24069
+ fs8.mkdirSync(dir, { recursive: true });
24070
+ if (persistentWorkspace && !options.workspaceDir) {
24071
+ fs8.mkdirSync(path9.join(dir, "memory"), { recursive: true });
24072
+ await clearTransient(dir);
24073
+ }
24074
+ const lockKey = persistentWorkspace || persistentSession ? `${def.id}:${scopeKey}` : `dir:${dir}`;
24075
+ if (inFlight.has(lockKey)) {
24076
+ logger.info(`[CodingAgent:${def.id}] already running \u2014 skipping this run`);
24077
+ return {
24078
+ agentId: def.id,
24079
+ runId,
24080
+ workspaceDir: dir,
24081
+ status: "skipped",
24082
+ answer: "",
24083
+ ms: 0,
24084
+ usage: { inputTokens: 0, outputTokens: 0, reasoningTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, toolCalls: 0 },
24085
+ error: "a run for this agent is already in progress"
24086
+ };
24087
+ }
24088
+ inFlight.add(lockKey);
24089
+ const state = loadState(scopeDir);
24090
+ let resumeId = options.resumeId;
24091
+ let turns = state.turns;
24092
+ if (persistentSession && !resumeId) {
24093
+ const overThreshold = policy.resetAfterTurns !== void 0 && state.turns >= policy.resetAfterTurns;
24094
+ if (overThreshold) {
24095
+ logger.info(
24096
+ `[CodingAgent:${def.id}] session reset after ${state.turns} runs (resetAfterTurns=${policy.resetAfterTurns}) \u2014 memory/ carries over`
24097
+ );
24098
+ turns = 0;
24099
+ } else {
24100
+ resumeId = state.sessionId;
24101
+ }
24102
+ }
24103
+ const ws = createWorkspace(dir, runId);
24104
+ const ctx = {
24105
+ projectId: options.projectId,
24106
+ userId: options.userId,
24107
+ runId,
24108
+ workspaceDir: dir,
24109
+ signal: options.signal,
24110
+ onOutput: options.onOutput,
24111
+ onEvent: options.onEvent,
24112
+ onUsage: options.onUsage
24113
+ };
24114
+ const input = options.input ?? {};
24115
+ const startedAt = Date.now();
24116
+ const record = {
24117
+ agentId: def.id,
24118
+ runId,
24119
+ workspaceDir: dir,
24120
+ status: "error",
24121
+ answer: "",
24122
+ ms: 0,
24123
+ usage: { inputTokens: 0, outputTokens: 0, reasoningTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, toolCalls: 0 }
24124
+ };
24125
+ try {
24126
+ await installInstructions(def, ws);
24127
+ if (def.prepare) await def.prepare(ws, input, ctx);
24128
+ const outputPath = def.output ? ws.pathTo(def.output.file) : void 0;
24129
+ if (outputPath) await fsp.rm(outputPath, { force: true });
24130
+ 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.");
24131
+ const tools = { ...cfg.sharedTools ?? {}, ...def.tools ?? {} };
24132
+ const session = createCodingAgentSession(def.harness || cfg.harness, {
24133
+ cwd: dir,
24134
+ model: def.model || cfg.model,
24135
+ resumeId,
24136
+ // undefined, not {}, when this agent has no tools — see the driver.
24137
+ mcpServers: tools && Object.keys(tools).length ? tools : void 0,
24138
+ ...def.networkAccessEnabled !== void 0 ? { networkAccessEnabled: def.networkAccessEnabled } : {},
24139
+ ...def.sandboxMode ? { sandboxMode: def.sandboxMode } : {}
24140
+ });
24141
+ const timeout = new AbortController();
24142
+ const timer = setTimeout(() => timeout.abort(), def.timeoutMs ?? DEFAULT_TIMEOUT_MS);
24143
+ const onOuterAbort = () => timeout.abort();
24144
+ options.signal?.addEventListener("abort", onOuterAbort, { once: true });
24145
+ let result;
24146
+ try {
24147
+ result = await session.run(
24148
+ prompt,
24149
+ {
24150
+ onOutput: ctx.onOutput,
24151
+ onEvent: ctx.onEvent,
24152
+ onUsage: ctx.onUsage,
24153
+ // Opt-in only: see stopOnDeliverable. Letting the turn end naturally
24154
+ // is what makes usage reporting work at all.
24155
+ ...outputPath && def.stopOnDeliverable ? { doneWhen: () => fs8.existsSync(outputPath) } : {}
24156
+ },
24157
+ timeout.signal
24158
+ );
24159
+ } finally {
24160
+ clearTimeout(timer);
24161
+ options.signal?.removeEventListener("abort", onOuterAbort);
24162
+ session.dispose();
24163
+ }
24164
+ record.answer = result.answer;
24165
+ record.ms = Date.now() - startedAt;
24166
+ record.usage = result.usage;
24167
+ record.sessionId = result.sessionId;
24168
+ if (persistentSession && result.sessionId) {
24169
+ saveState(scopeDir, { sessionId: result.sessionId, turns: turns + 1, updatedAt: "" });
24170
+ }
24171
+ if (result.status === "aborted") {
24172
+ record.status = "aborted";
24173
+ record.error = options.signal?.aborted ? "aborted" : "timed out";
24174
+ return record;
24175
+ }
24176
+ if (result.status === "error") {
24177
+ record.error = result.error ?? "agent failed";
24178
+ return record;
24179
+ }
24180
+ const successStatus = result.status === "early" ? "early" : "completed";
24181
+ if (!def.output) {
24182
+ record.status = successStatus;
24183
+ return record;
24184
+ }
24185
+ const raw = await ws.read(def.output.file);
24186
+ if (raw === null) {
24187
+ record.error = `agent finished without writing ${def.output.file}`;
24188
+ return record;
24189
+ }
24190
+ let parsed;
24191
+ try {
24192
+ parsed = def.output.schema.parse(JSON.parse(raw));
24193
+ } catch (err) {
24194
+ record.error = `deliverable failed validation: ${err?.message ?? err}`;
24195
+ return record;
24196
+ }
24197
+ record.output = parsed;
24198
+ if (def.apply) {
24199
+ record.applied = await def.apply(parsed, input, ctx);
24200
+ }
24201
+ record.status = successStatus;
24202
+ return record;
24203
+ } catch (err) {
24204
+ record.error = err?.message ?? String(err);
24205
+ record.ms = Date.now() - startedAt;
24206
+ return record;
24207
+ } finally {
24208
+ inFlight.delete(lockKey);
24209
+ logger.info(
24210
+ `[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}"` : "")
24211
+ );
24212
+ if (!options.workspaceDir && !persistentWorkspace) await pruneRuns(path9.join(scopeDir, "runs"), cfg.keepRuns);
24213
+ }
24214
+ }
24215
+
24216
+ // src/codingAgent/tools/bridge-shims.ts
24217
+ import * as fs9 from "fs";
24218
+
24219
+ // src/codingAgent/tools/read-only-sql.ts
24220
+ var WRITE_KEYWORDS = [
24221
+ "insert",
24222
+ "update",
24223
+ "delete",
24224
+ "drop",
24225
+ "alter",
24226
+ "create",
24227
+ "truncate",
24228
+ "replace",
24229
+ "grant",
24230
+ "revoke",
24231
+ "merge",
24232
+ "upsert",
24233
+ "call",
24234
+ "exec",
24235
+ "execute",
24236
+ "copy",
24237
+ "vacuum",
24238
+ "analyze",
24239
+ "comment",
24240
+ "lock",
24241
+ "set",
24242
+ "commit",
24243
+ "rollback",
24244
+ "begin"
24245
+ ];
24246
+ function stripSqlNoise(sql) {
24247
+ return sql.replace(/--[^\n]*/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/'(?:[^']|'')*'/g, "''").replace(/"(?:[^"]|"")*"/g, '""');
24248
+ }
24249
+ function assertReadOnlySql(sql) {
24250
+ const bare = stripSqlNoise(sql).trim();
24251
+ if (!bare) throw new Error("Empty SQL.");
24252
+ if (bare.replace(/;\s*$/, "").includes(";")) {
24253
+ throw new Error('Only a single statement is allowed \u2014 remove the extra ";".');
24254
+ }
24255
+ if (!/^\s*(select|with)\b/i.test(bare)) {
24256
+ throw new Error("Read-only: the statement must start with SELECT or WITH.");
24257
+ }
24258
+ if (/\binto\s+\w/i.test(bare)) {
24259
+ throw new Error("Read-only: SELECT ... INTO is not allowed.");
24260
+ }
24261
+ for (const kw of WRITE_KEYWORDS) {
24262
+ if (new RegExp(`\\b${kw}\\b`, "i").test(bare)) {
24263
+ throw new Error(`Read-only: "${kw.toUpperCase()}" is not allowed. Only SELECT / WITH queries.`);
24264
+ }
24265
+ }
24266
+ }
24267
+ var READ_ONLY_SQL_SOURCE = `
24268
+ const WRITE_KEYWORDS = ${JSON.stringify(WRITE_KEYWORDS)};
24269
+ ${stripSqlNoise.toString()}
24270
+ ${assertReadOnlySql.toString().replace("export function", "function")}
24271
+ `;
24272
+
24273
+ // src/codingAgent/tools/bridge-shims.ts
24274
+ function writeBridgeShims(ws, opts) {
24275
+ const maxRows = opts.maxRows ?? 200;
24276
+ const query = `#!/usr/bin/env node
24277
+ // Read-only SQL against a data source. Generated per run \u2014 do not edit.
24278
+ ${READ_ONLY_SQL_SOURCE}
24279
+ const BRIDGE = ${JSON.stringify(opts.bridgeUrl)};
24280
+ const MAX_ROWS = ${maxRows};
24281
+
24282
+ const [toolId, sql, limitArg] = process.argv.slice(2);
24283
+ if (!toolId || !sql) {
24284
+ console.error('usage: ./query "<toolId>" "<sql>" [limit]\\nRun ./sources for the toolIds.');
24285
+ process.exit(2);
24286
+ }
24287
+ try { assertReadOnlySql(sql); } catch (e) { console.error(String(e.message || e)); process.exit(2); }
24288
+
24289
+ fetch(BRIDGE + '/query', {
24290
+ method: 'POST',
24291
+ headers: { 'content-type': 'application/json' },
24292
+ body: JSON.stringify({ toolId, sql, limit: Math.min(Number(limitArg) || MAX_ROWS, MAX_ROWS) }),
24293
+ })
24294
+ .then(async (r) => {
24295
+ const text = await r.text();
24296
+ if (!r.ok) { console.error('Query failed (' + r.status + '): ' + text.slice(0, 2000)); process.exit(1); }
24297
+ let p; try { p = JSON.parse(text); } catch { console.log(text); return; }
24298
+ if (p && p.error) { console.error(String(p.error)); process.exit(1); }
24299
+ const rows = (p && (p.data || p.rows)) || p;
24300
+ const all = Array.isArray(rows) ? rows : [rows];
24301
+ // Capped so one careless SELECT * cannot flood the agent's context.
24302
+ console.log(JSON.stringify({
24303
+ count: (p && p.count) != null ? p.count : all.length,
24304
+ truncated: all.length > MAX_ROWS,
24305
+ rows: all.slice(0, MAX_ROWS),
24306
+ }, null, 2));
24307
+ })
24308
+ .catch((e) => { console.error('Could not reach the data bridge: ' + (e && e.message)); process.exit(1); });
24309
+ `;
24310
+ const sources = `#!/usr/bin/env node
24311
+ // The data sources you can query, and the exact toolId for ./query.
24312
+ console.log(${JSON.stringify(JSON.stringify(opts.sources, null, 2))});
24313
+ `;
24314
+ for (const [name, body] of [["query", query], ["sources", sources]]) {
24315
+ const p = ws.pathTo(name);
24316
+ fs9.writeFileSync(p, body, "utf-8");
24317
+ fs9.chmodSync(p, 493);
24318
+ }
24319
+ }
24320
+
24321
+ // src/codingAgent/triggers.ts
24322
+ import * as fs10 from "fs";
24323
+ import * as path10 from "path";
24324
+ function statePath2(agentId) {
24325
+ return path10.join(getCodingAgentsConfig().runtimeDir, agentId, "_triggers.json");
24326
+ }
24327
+ function loadTriggerState(agentId) {
24328
+ try {
24329
+ return JSON.parse(fs10.readFileSync(statePath2(agentId), "utf-8"));
24330
+ } catch {
24331
+ return { counts: {} };
24332
+ }
24333
+ }
24334
+ function saveTriggerState(agentId, state) {
24335
+ try {
24336
+ const p = statePath2(agentId);
24337
+ fs10.mkdirSync(path10.dirname(p), { recursive: true });
24338
+ fs10.writeFileSync(p, JSON.stringify(state, null, 2));
24339
+ } catch (err) {
24340
+ logger.warn(`[CodingAgent:${agentId}] failed to persist trigger state: ${err?.message ?? err}`);
24341
+ }
24342
+ }
24343
+ var asArray = (t) => !t ? [] : Array.isArray(t) ? t : [t];
24344
+ var intervals = /* @__PURE__ */ new Map();
24345
+ var debounces = /* @__PURE__ */ new Map();
24346
+ var firing = /* @__PURE__ */ new Set();
24347
+ var listeners = [];
24348
+ function onAgentRun(fn) {
24349
+ listeners.push(fn);
24350
+ return () => {
24351
+ const i = listeners.indexOf(fn);
24352
+ if (i >= 0) listeners.splice(i, 1);
24353
+ };
24354
+ }
24355
+ async function fire(agentId, reason, payload) {
24356
+ if (firing.has(agentId)) {
24357
+ const st = loadTriggerState(agentId);
24358
+ saveTriggerState(agentId, { ...st, pending: true });
24359
+ logger.info(`[CodingAgent:${agentId}] ${reason} arrived mid-run \u2014 queued`);
24360
+ return;
24361
+ }
24362
+ firing.add(agentId);
24363
+ try {
24364
+ logger.info(`[CodingAgent:${agentId}] triggered by ${reason}`);
24365
+ const record = await runCodingAgent(agentId, { input: payload });
24366
+ saveTriggerState(agentId, { ...loadTriggerState(agentId), lastRunAt: (/* @__PURE__ */ new Date()).toISOString(), pending: false });
24367
+ for (const fn of listeners) {
24368
+ try {
24369
+ fn(record, { agentId, reason, payload });
24370
+ } catch {
24371
+ }
24372
+ }
24373
+ } catch (err) {
24374
+ logger.error(`[CodingAgent:${agentId}] trigger run failed: ${err?.message ?? err}`);
24375
+ } finally {
24376
+ firing.delete(agentId);
24377
+ if (loadTriggerState(agentId).pending) {
24378
+ saveTriggerState(agentId, { ...loadTriggerState(agentId), pending: false });
24379
+ void fire(agentId, "queued work from during the last run");
24380
+ }
24381
+ }
24382
+ }
24383
+ function emitAgentEvent(event, payload) {
24384
+ for (const { id } of listCodingAgents()) {
24385
+ const def = getCodingAgent(id);
24386
+ if (!def) continue;
24387
+ for (const trigger of asArray(def.trigger)) {
24388
+ if (trigger.type === "event") {
24389
+ const names = Array.isArray(trigger.on) ? trigger.on : [trigger.on];
24390
+ if (!names.includes(event)) continue;
24391
+ if (!trigger.debounceMs) {
24392
+ void fire(id, `event ${event}`, payload);
24393
+ continue;
24394
+ }
24395
+ const key = `${id}:${event}`;
24396
+ const existing = debounces.get(key);
24397
+ if (existing) clearTimeout(existing);
24398
+ debounces.set(key, setTimeout(() => {
24399
+ debounces.delete(key);
24400
+ void fire(id, `event ${event} (settled)`, payload);
24401
+ }, trigger.debounceMs));
24402
+ continue;
24403
+ }
24404
+ if (trigger.type === "count" && trigger.on === event) {
24405
+ const state = loadTriggerState(id);
24406
+ const next = (state.counts[event] ?? 0) + 1;
24407
+ if (next < trigger.every) {
24408
+ saveTriggerState(id, { ...state, counts: { ...state.counts, [event]: next } });
24409
+ continue;
24410
+ }
24411
+ saveTriggerState(id, { ...state, counts: { ...state.counts, [event]: 0 } });
24412
+ void fire(id, `${trigger.every}x ${event}`, payload);
24413
+ }
24414
+ }
24415
+ }
24416
+ }
24417
+ function startAgentTriggers() {
24418
+ stopAgentTriggers();
24419
+ for (const { id } of listCodingAgents()) {
24420
+ const def = getCodingAgent(id);
24421
+ for (const trigger of asArray(def?.trigger)) {
24422
+ if (trigger.type !== "interval") continue;
24423
+ intervals.set(id, setInterval(() => void fire(id, `interval ${trigger.everyMs}ms`), trigger.everyMs));
24424
+ logger.info(`[CodingAgent:${id}] interval trigger every ${trigger.everyMs}ms`);
24425
+ }
24426
+ }
24427
+ }
24428
+ function stopAgentTriggers() {
24429
+ for (const t of intervals.values()) clearInterval(t);
24430
+ intervals.clear();
24431
+ for (const t of debounces.values()) clearTimeout(t);
24432
+ debounces.clear();
24433
+ }
24434
+ function describeTriggers() {
24435
+ return listCodingAgents().map(({ id }) => {
24436
+ const def = getCodingAgent(id);
24437
+ return {
24438
+ agentId: id,
24439
+ triggers: asArray(def?.trigger).map(
24440
+ (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"
24441
+ )
24442
+ };
24443
+ });
24444
+ }
24445
+
24446
+ // src/codingAgent/collection.ts
24447
+ import * as fs11 from "fs";
24448
+ import * as path11 from "path";
24449
+ function registerCodingAgentCollection(sdk, config2 = {}) {
24450
+ const runtimeDir = config2.runtimeDir || process.env.CODING_AGENT_RUNTIME_DIR || path11.join(process.cwd(), ".coding-agent-runtime");
24451
+ const defaultHarness = config2.harness || process.env.CODING_AGENT_HARNESS || "codex";
24452
+ const sessions = /* @__PURE__ */ new Map();
24453
+ const running = /* @__PURE__ */ new Set();
24454
+ sdk.addCollection("coding-agent", "run", async (params) => {
24455
+ const { prompt, threadId, agentId, projectId, userId, harness, resumeId, signal, onOutput, onEvent, onUsage } = params;
24456
+ if (!prompt || !threadId) {
24457
+ return { success: false, error: "prompt and threadId are required" };
24458
+ }
24459
+ if (running.has(threadId)) {
24460
+ return { success: false, error: "This thread already has a turn in progress." };
24461
+ }
24462
+ running.add(threadId);
24463
+ try {
24464
+ if (agentId) {
24465
+ const def = getCodingAgent(agentId);
24466
+ if (!def) {
24467
+ return {
24468
+ success: false,
24469
+ error: `Unknown agent "${agentId}". Available: ${listCodingAgents().map((a) => a.id).join(", ") || "(none registered)"}`
24470
+ };
24471
+ }
24472
+ const record = await runCodingAgent(agentId, {
24473
+ prompt,
24474
+ projectId,
24475
+ userId,
24476
+ threadId,
24477
+ signal,
24478
+ onOutput,
24479
+ onEvent,
24480
+ onUsage,
24481
+ ...resumeId ? { resumeId } : {}
24482
+ });
24483
+ return {
24484
+ success: record.status === "completed" || record.status === "early",
24485
+ status: record.status,
24486
+ answer: record.answer,
24487
+ ms: record.ms,
24488
+ usage: record.usage,
24489
+ sessionId: record.sessionId,
24490
+ ...record.output !== void 0 ? { output: record.output } : {},
24491
+ ...record.applied ? { applied: record.applied } : {},
24492
+ ...record.error ? { error: record.error } : {}
24493
+ };
24494
+ }
24495
+ const cwd = path11.join(runtimeDir, "threads", threadId);
24496
+ fs11.mkdirSync(cwd, { recursive: true });
24497
+ let session = sessions.get(threadId);
24498
+ if (!session) {
24499
+ session = createCodingAgentSession(harness || defaultHarness, {
24500
+ cwd,
24501
+ model: config2.model,
24502
+ resumeId,
24503
+ mcpServers: config2.mcpServers
24504
+ });
24505
+ sessions.set(threadId, session);
24506
+ }
24507
+ const result = await session.run(prompt, { onOutput, onEvent, onUsage }, signal);
24508
+ logger.info(
24509
+ `[CodingAgent] thread=${threadId} harness=${session.harness} status=${result.status} ms=${result.ms} tokens=${result.usage.inputTokens}/${result.usage.outputTokens} tools=${result.usage.toolCalls}`
24510
+ );
24511
+ return {
24512
+ // An abort is the user's own choice, not a failure — but it is also
24513
+ // not a completed answer, so `status` is what the UI should branch on.
24514
+ success: result.status === "completed" || result.status === "early",
24515
+ status: result.status,
24516
+ answer: result.answer,
24517
+ ms: result.ms,
24518
+ usage: result.usage,
24519
+ sessionId: result.sessionId,
24520
+ ...result.error ? { error: result.error } : {}
24521
+ };
24522
+ } catch (error) {
24523
+ const message = error?.message ?? String(error);
24524
+ logger.error(`[CodingAgent] thread=${threadId} failed: ${message}`);
24525
+ return { success: false, status: "error", error: message };
24526
+ } finally {
24527
+ running.delete(threadId);
24528
+ }
24529
+ });
24530
+ sdk.addCollection("coding-agent", "reset", async (params) => {
24531
+ const session = sessions.get(params?.threadId);
24532
+ if (session) {
24533
+ session.reset();
24534
+ session.dispose();
24535
+ sessions.delete(params.threadId);
24536
+ }
24537
+ return { success: true };
24538
+ });
24539
+ sdk.addCollection("coding-agent", "list", async () => ({ success: true, agents: listCodingAgents() }));
24540
+ console.log(`\u2713 coding-agent collection registered (harness: ${defaultHarness})`);
24541
+ }
24542
+
24543
+ // src/codingAgent/handler.ts
24544
+ var inFlightAborts3 = /* @__PURE__ */ new Map();
24545
+ var recentlyAborted3 = /* @__PURE__ */ new Map();
24546
+ var ABORT_TOMBSTONE_TTL_MS3 = 3e4;
24547
+ function setAbortTombstone3(threadId) {
24548
+ const now = Date.now();
24549
+ recentlyAborted3.set(threadId, now);
24550
+ for (const [key, ts] of recentlyAborted3) {
24551
+ if (now - ts > ABORT_TOMBSTONE_TTL_MS3) recentlyAborted3.delete(key);
24552
+ }
24553
+ }
24554
+ function consumeAbortTombstone3(threadId) {
24555
+ const ts = recentlyAborted3.get(threadId);
24556
+ if (ts === void 0) return false;
24557
+ recentlyAborted3.delete(threadId);
24558
+ return Date.now() - ts <= ABORT_TOMBSTONE_TTL_MS3;
24559
+ }
24560
+ function handleCodingAgentAbort(data, sendMessage) {
24561
+ const parseResult = CodingAgentAbortRequestMessageSchema.safeParse(data);
24562
+ if (!parseResult.success) {
24563
+ logger.warn("[CodingAgentAbort] Invalid message \u2014 ignoring");
24564
+ return;
24565
+ }
24566
+ const { payload, from } = parseResult.data;
24567
+ const { threadId } = payload;
24568
+ const controller = inFlightAborts3.get(threadId);
24569
+ if (controller) {
24570
+ controller.abort();
24571
+ inFlightAborts3.delete(threadId);
24572
+ logger.info(`[CodingAgentAbort] Aborted in-flight run for thread ${threadId}`);
24573
+ } else {
24574
+ setAbortTombstone3(threadId);
24575
+ logger.info(`[CodingAgentAbort] No in-flight run for thread ${threadId} \u2014 tombstoned`);
24576
+ }
24577
+ try {
24578
+ sendMessage({
24579
+ id: `coding_agent_aborted_${threadId}`,
24580
+ type: "CODING_AGENT_ABORTED",
24581
+ from: { type: "data-agent" },
24582
+ to: { type: "runtime", id: from.id },
24583
+ payload: { threadId }
24584
+ });
24585
+ } catch (err) {
24586
+ logger.warn(`[CodingAgentAbort] Failed to send abort ack: ${err instanceof Error ? err.message : String(err)}`);
24587
+ }
24588
+ }
24589
+ async function handleCodingAgentRequest(data, collections, sendMessage) {
24590
+ const parseResult = CodingAgentRequestMessageSchema.safeParse(data);
24591
+ if (!parseResult.success) {
24592
+ logger.error("[CodingAgentReq] Invalid message:", JSON.stringify(parseResult.error.errors));
24593
+ return;
24594
+ }
24595
+ const { id, payload, from } = parseResult.data;
24596
+ const { prompt, threadId, projectId, userId, agentId, harness, resumeId } = payload;
24597
+ const wsId = from.id;
24598
+ const respond = (res) => {
24599
+ sendMessage({
24600
+ id,
24601
+ type: "CODING_AGENT_RES",
24602
+ from: { type: "data-agent" },
24603
+ to: { type: "runtime", id: wsId },
24604
+ payload: res
24605
+ });
24606
+ };
24607
+ if (!collections?.["coding-agent"]?.["run"]) {
24608
+ respond({ success: false, error: "Coding agent is not available in this environment (collection 'coding-agent' not registered)." });
24609
+ return;
24610
+ }
24611
+ const abortController = new AbortController();
24612
+ if (consumeAbortTombstone3(threadId)) {
24613
+ abortController.abort();
24614
+ logger.info(`[CodingAgentReq] thread ${threadId} was pre-aborted \u2014 starting cancelled`);
24615
+ }
24616
+ inFlightAborts3.set(threadId, abortController);
24617
+ let seq = 0;
24618
+ const stream = (frame) => {
24619
+ try {
24620
+ sendMessage({
24621
+ // Keyed by this request's id, not threadId — threadId repeats across
24622
+ // every turn in a chat, so a listener that failed to clean up would
24623
+ // keep matching future turns forever.
24624
+ id: `coding_agent_stream_${id}`,
24625
+ type: "CODING_AGENT_STREAM",
24626
+ from: { type: "data-agent" },
24627
+ to: { type: "runtime", id: wsId },
24628
+ payload: { threadId, seq: seq++, ...frame }
24629
+ });
24630
+ } catch (err) {
24631
+ logger.warn(`[CodingAgentReq] Stream send failed (socket may be closed): ${err instanceof Error ? err.message : String(err)}`);
24632
+ }
24633
+ };
24634
+ try {
24635
+ const result = await collections["coding-agent"]["run"]({
24636
+ prompt,
24637
+ threadId,
24638
+ projectId,
24639
+ userId,
24640
+ agentId,
24641
+ harness,
24642
+ resumeId,
24643
+ signal: abortController.signal,
24644
+ onOutput: (chunk) => stream({ chunk }),
24645
+ onEvent: (event) => stream({ event }),
24646
+ onUsage: (usage) => stream({ usage })
24647
+ });
24648
+ respond(result ?? { success: false, error: "Coding agent returned no result" });
24649
+ } catch (error) {
24650
+ const errorMsg = error instanceof Error ? error.message : String(error);
24651
+ logger.error(`[CodingAgentReq] Error: ${errorMsg}`);
24652
+ respond({ success: false, error: abortController.signal.aborted ? "aborted" : errorMsg });
24653
+ } finally {
24654
+ inFlightAborts3.delete(threadId);
24655
+ }
24656
+ }
24657
+
23602
24658
  // src/reportComp/types.ts
23603
24659
  var DEFAULT_REPORT_COMP_MODELS = {
23604
24660
  anthropic: "anthropic/claude-haiku-4-5-20251001",
@@ -24097,21 +25153,21 @@ Fixed SQL query:`;
24097
25153
  }
24098
25154
 
24099
25155
  // src/reportComp/index.ts
24100
- var inFlightAborts3 = /* @__PURE__ */ new Map();
24101
- var recentlyAborted3 = /* @__PURE__ */ new Map();
24102
- var ABORT_TOMBSTONE_TTL_MS3 = 3e4;
24103
- function setAbortTombstone3(reportId) {
25156
+ var inFlightAborts4 = /* @__PURE__ */ new Map();
25157
+ var recentlyAborted4 = /* @__PURE__ */ new Map();
25158
+ var ABORT_TOMBSTONE_TTL_MS4 = 3e4;
25159
+ function setAbortTombstone4(reportId) {
24104
25160
  const now = Date.now();
24105
- recentlyAborted3.set(reportId, now);
24106
- for (const [key, ts] of recentlyAborted3) {
24107
- if (now - ts > ABORT_TOMBSTONE_TTL_MS3) recentlyAborted3.delete(key);
25161
+ recentlyAborted4.set(reportId, now);
25162
+ for (const [key, ts] of recentlyAborted4) {
25163
+ if (now - ts > ABORT_TOMBSTONE_TTL_MS4) recentlyAborted4.delete(key);
24108
25164
  }
24109
25165
  }
24110
- function consumeAbortTombstone3(reportId) {
24111
- const ts = recentlyAborted3.get(reportId);
25166
+ function consumeAbortTombstone4(reportId) {
25167
+ const ts = recentlyAborted4.get(reportId);
24112
25168
  if (ts === void 0) return false;
24113
- recentlyAborted3.delete(reportId);
24114
- return Date.now() - ts <= ABORT_TOMBSTONE_TTL_MS3;
25169
+ recentlyAborted4.delete(reportId);
25170
+ return Date.now() - ts <= ABORT_TOMBSTONE_TTL_MS4;
24115
25171
  }
24116
25172
  function handleReportCompAbort(data, sendMessage) {
24117
25173
  const parseResult = ReportCompAbortRequestMessageSchema.safeParse(data);
@@ -24122,13 +25178,13 @@ function handleReportCompAbort(data, sendMessage) {
24122
25178
  const { payload, from } = parseResult.data;
24123
25179
  const { reportId } = payload;
24124
25180
  const wsId = from.id;
24125
- const controller = inFlightAborts3.get(reportId);
25181
+ const controller = inFlightAborts4.get(reportId);
24126
25182
  if (controller) {
24127
25183
  controller.abort();
24128
- inFlightAborts3.delete(reportId);
25184
+ inFlightAborts4.delete(reportId);
24129
25185
  logger.info(`[ReportCompAbort] Aborted in-flight run for report ${reportId}`);
24130
25186
  } else {
24131
- setAbortTombstone3(reportId);
25187
+ setAbortTombstone4(reportId);
24132
25188
  logger.info(`[ReportCompAbort] No in-flight run for report ${reportId} \u2014 tombstoned`);
24133
25189
  }
24134
25190
  try {
@@ -24174,11 +25230,11 @@ var processReportCompRequest = async (data, components, _sendMessage, anthropicA
24174
25230
  const trace = createRunTrace();
24175
25231
  const abortController = new AbortController();
24176
25232
  if (reportId) {
24177
- if (consumeAbortTombstone3(reportId)) {
25233
+ if (consumeAbortTombstone4(reportId)) {
24178
25234
  abortController.abort();
24179
25235
  logger.info(`[REPORT_COMP_REQ] reportId=${reportId} was pre-aborted \u2014 starting cancelled`);
24180
25236
  }
24181
- inFlightAborts3.set(reportId, abortController);
25237
+ inFlightAborts4.set(reportId, abortController);
24182
25238
  }
24183
25239
  let llmResponse;
24184
25240
  try {
@@ -24199,7 +25255,7 @@ var processReportCompRequest = async (data, components, _sendMessage, anthropicA
24199
25255
  trace
24200
25256
  );
24201
25257
  } finally {
24202
- if (reportId) inFlightAborts3.delete(reportId);
25258
+ if (reportId) inFlightAborts4.delete(reportId);
24203
25259
  }
24204
25260
  const wasAborted = abortController.signal.aborted || !llmResponse.success && Array.isArray(llmResponse.errors) && llmResponse.errors.includes("aborted");
24205
25261
  if (wasAborted) {
@@ -24400,7 +25456,7 @@ function pick(source, keys) {
24400
25456
  }
24401
25457
  return out;
24402
25458
  }
24403
- function asArray(value) {
25459
+ function asArray2(value) {
24404
25460
  return Array.isArray(value) ? value : [];
24405
25461
  }
24406
25462
  function sanitizeField(field) {
@@ -24461,7 +25517,7 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24461
25517
  lines.push(`Row Count: ~${entity.rowCount.toLocaleString()}`);
24462
25518
  }
24463
25519
  lines.push("Columns:");
24464
- for (const field of asArray(entity.fields).concat(asArray(entity.columns))) {
25520
+ for (const field of asArray2(entity.fields).concat(asArray2(entity.columns))) {
24465
25521
  let line = ` - ${field.name}: ${field.type}`;
24466
25522
  if (field.isPrimaryKey) line += " (PRIMARY KEY)";
24467
25523
  if (field.isForeignKey && field.references) {
@@ -24479,7 +25535,7 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24479
25535
  lines.push("TABLE RELATIONSHIPS:");
24480
25536
  lines.push("");
24481
25537
  for (const rel of relationships) {
24482
- lines.push(`${rel.from} -> ${rel.to} (${rel.type}): ${asArray(rel.keys).join(" = ")}`);
25538
+ lines.push(`${rel.from} -> ${rel.to} (${rel.type}): ${asArray2(rel.keys).join(" = ")}`);
24483
25539
  }
24484
25540
  lines.push("");
24485
25541
  };
@@ -24487,10 +25543,10 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24487
25543
  if (sources) {
24488
25544
  for (const source of sources) {
24489
25545
  renderEntities(
24490
- asArray(source.schema?.entities).concat(asArray(source.schema?.tables)),
25546
+ asArray2(source.schema?.entities).concat(asArray2(source.schema?.tables)),
24491
25547
  `${source.name} (${source.type})`
24492
25548
  );
24493
- renderRelationships(asArray(source.schema?.relationships));
25549
+ renderRelationships(asArray2(source.schema?.relationships));
24494
25550
  }
24495
25551
  return lines.join("\n");
24496
25552
  }
@@ -24498,8 +25554,8 @@ function generateSanitizedSchemaDocumentation(sanitized) {
24498
25554
  if (typeof sanitized.schema === "string") lines.push(`Schema: ${sanitized.schema}`);
24499
25555
  if (sanitized.description) lines.push(`Description: ${sanitized.description}`);
24500
25556
  lines.push("");
24501
- renderEntities(asArray(sanitized.entities).concat(asArray(sanitized.tables)));
24502
- renderRelationships(asArray(sanitized.relationships));
25557
+ renderEntities(asArray2(sanitized.entities).concat(asArray2(sanitized.tables)));
25558
+ renderRelationships(asArray2(sanitized.relationships));
24503
25559
  return lines.join("\n");
24504
25560
  }
24505
25561
 
@@ -24585,8 +25641,8 @@ function getLLMProviders() {
24585
25641
  }
24586
25642
 
24587
25643
  // src/auth/user-manager.ts
24588
- import fs8 from "fs";
24589
- import path8 from "path";
25644
+ import fs12 from "fs";
25645
+ import path12 from "path";
24590
25646
  import os2 from "os";
24591
25647
  var UserManager = class {
24592
25648
  /**
@@ -24599,7 +25655,7 @@ var UserManager = class {
24599
25655
  this.hasChanged = false;
24600
25656
  this.syncInterval = null;
24601
25657
  this.isInitialized = false;
24602
- this.filePath = path8.join(os2.homedir(), ".superatom", "projects", projectId, "users.json");
25658
+ this.filePath = path12.join(os2.homedir(), ".superatom", "projects", projectId, "users.json");
24603
25659
  this.syncIntervalMs = syncIntervalMs;
24604
25660
  }
24605
25661
  /**
@@ -24624,20 +25680,20 @@ var UserManager = class {
24624
25680
  */
24625
25681
  async loadUsersFromFile() {
24626
25682
  try {
24627
- const dir = path8.dirname(this.filePath);
24628
- if (!fs8.existsSync(dir)) {
25683
+ const dir = path12.dirname(this.filePath);
25684
+ if (!fs12.existsSync(dir)) {
24629
25685
  logger.info(`Creating directory structure: ${dir}`);
24630
- fs8.mkdirSync(dir, { recursive: true });
25686
+ fs12.mkdirSync(dir, { recursive: true });
24631
25687
  }
24632
- if (!fs8.existsSync(this.filePath)) {
25688
+ if (!fs12.existsSync(this.filePath)) {
24633
25689
  logger.info(`Users file does not exist at ${this.filePath}, creating with empty users`);
24634
25690
  const initialData = { users: [] };
24635
- fs8.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
25691
+ fs12.writeFileSync(this.filePath, JSON.stringify(initialData, null, 4));
24636
25692
  this.users = [];
24637
25693
  this.hasChanged = false;
24638
25694
  return;
24639
25695
  }
24640
- const fileContent = fs8.readFileSync(this.filePath, "utf-8");
25696
+ const fileContent = fs12.readFileSync(this.filePath, "utf-8");
24641
25697
  const rawData = JSON.parse(fileContent);
24642
25698
  const validatedData = UsersDataSchema.parse(rawData);
24643
25699
  this.users = validatedData.users;
@@ -24656,16 +25712,16 @@ var UserManager = class {
24656
25712
  return;
24657
25713
  }
24658
25714
  try {
24659
- const dir = path8.dirname(this.filePath);
24660
- if (!fs8.existsSync(dir)) {
24661
- fs8.mkdirSync(dir, { recursive: true });
25715
+ const dir = path12.dirname(this.filePath);
25716
+ if (!fs12.existsSync(dir)) {
25717
+ fs12.mkdirSync(dir, { recursive: true });
24662
25718
  }
24663
25719
  const usersToSave = this.users.map((user) => {
24664
25720
  const { wsIds, ...userWithoutWsIds } = user;
24665
25721
  return userWithoutWsIds;
24666
25722
  });
24667
25723
  const data = { users: usersToSave };
24668
- fs8.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
25724
+ fs12.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
24669
25725
  this.hasChanged = false;
24670
25726
  logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
24671
25727
  } catch (error) {
@@ -24883,8 +25939,8 @@ var UserManager = class {
24883
25939
  };
24884
25940
 
24885
25941
  // src/dashboards/dashboard-manager.ts
24886
- import fs9 from "fs";
24887
- import path9 from "path";
25942
+ import fs13 from "fs";
25943
+ import path13 from "path";
24888
25944
  import os3 from "os";
24889
25945
  var DashboardManager = class {
24890
25946
  /**
@@ -24893,7 +25949,7 @@ var DashboardManager = class {
24893
25949
  */
24894
25950
  constructor(projectId = "snowflake-dataset") {
24895
25951
  this.projectId = projectId;
24896
- this.dashboardsBasePath = path9.join(
25952
+ this.dashboardsBasePath = path13.join(
24897
25953
  os3.homedir(),
24898
25954
  ".superatom",
24899
25955
  "projects",
@@ -24907,7 +25963,7 @@ var DashboardManager = class {
24907
25963
  * @returns Full path to dashboard data.json file
24908
25964
  */
24909
25965
  getDashboardPath(dashboardId) {
24910
- return path9.join(this.dashboardsBasePath, dashboardId, "data.json");
25966
+ return path13.join(this.dashboardsBasePath, dashboardId, "data.json");
24911
25967
  }
24912
25968
  /**
24913
25969
  * Create a new dashboard
@@ -24917,13 +25973,13 @@ var DashboardManager = class {
24917
25973
  */
24918
25974
  createDashboard(dashboardId, dashboard) {
24919
25975
  const dashboardPath = this.getDashboardPath(dashboardId);
24920
- const dashboardDir = path9.dirname(dashboardPath);
24921
- if (fs9.existsSync(dashboardPath)) {
25976
+ const dashboardDir = path13.dirname(dashboardPath);
25977
+ if (fs13.existsSync(dashboardPath)) {
24922
25978
  throw new Error(`Dashboard '${dashboardId}' already exists`);
24923
25979
  }
24924
25980
  const validated = DSLRendererPropsSchema.parse(dashboard);
24925
- fs9.mkdirSync(dashboardDir, { recursive: true });
24926
- fs9.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
25981
+ fs13.mkdirSync(dashboardDir, { recursive: true });
25982
+ fs13.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
24927
25983
  logger.info(`Dashboard created: ${dashboardId}`);
24928
25984
  return validated;
24929
25985
  }
@@ -24934,12 +25990,12 @@ var DashboardManager = class {
24934
25990
  */
24935
25991
  getDashboard(dashboardId) {
24936
25992
  const dashboardPath = this.getDashboardPath(dashboardId);
24937
- if (!fs9.existsSync(dashboardPath)) {
25993
+ if (!fs13.existsSync(dashboardPath)) {
24938
25994
  logger.warn(`Dashboard not found: ${dashboardId}`);
24939
25995
  return null;
24940
25996
  }
24941
25997
  try {
24942
- const fileContent = fs9.readFileSync(dashboardPath, "utf-8");
25998
+ const fileContent = fs13.readFileSync(dashboardPath, "utf-8");
24943
25999
  const dashboard = JSON.parse(fileContent);
24944
26000
  const validated = DSLRendererPropsSchema.parse(dashboard);
24945
26001
  return validated;
@@ -24953,16 +26009,16 @@ var DashboardManager = class {
24953
26009
  * @returns Array of dashboard objects with their IDs
24954
26010
  */
24955
26011
  getAllDashboards() {
24956
- if (!fs9.existsSync(this.dashboardsBasePath)) {
24957
- fs9.mkdirSync(this.dashboardsBasePath, { recursive: true });
26012
+ if (!fs13.existsSync(this.dashboardsBasePath)) {
26013
+ fs13.mkdirSync(this.dashboardsBasePath, { recursive: true });
24958
26014
  return [];
24959
26015
  }
24960
26016
  const dashboards = [];
24961
26017
  try {
24962
- const dashboardDirs = fs9.readdirSync(this.dashboardsBasePath);
26018
+ const dashboardDirs = fs13.readdirSync(this.dashboardsBasePath);
24963
26019
  for (const dashboardId of dashboardDirs) {
24964
26020
  const dashboardPath = this.getDashboardPath(dashboardId);
24965
- if (fs9.existsSync(dashboardPath)) {
26021
+ if (fs13.existsSync(dashboardPath)) {
24966
26022
  const dashboard = this.getDashboard(dashboardId);
24967
26023
  if (dashboard) {
24968
26024
  dashboards.push({ dashboardId, dashboard });
@@ -24984,13 +26040,13 @@ var DashboardManager = class {
24984
26040
  */
24985
26041
  updateDashboard(dashboardId, dashboard) {
24986
26042
  const dashboardPath = this.getDashboardPath(dashboardId);
24987
- if (!fs9.existsSync(dashboardPath)) {
26043
+ if (!fs13.existsSync(dashboardPath)) {
24988
26044
  logger.warn(`Dashboard not found for update: ${dashboardId}`);
24989
26045
  return null;
24990
26046
  }
24991
26047
  try {
24992
26048
  const validated = DSLRendererPropsSchema.parse(dashboard);
24993
- fs9.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
26049
+ fs13.writeFileSync(dashboardPath, JSON.stringify(validated, null, 4));
24994
26050
  logger.info(`Dashboard updated: ${dashboardId}`);
24995
26051
  return validated;
24996
26052
  } catch (error) {
@@ -25005,13 +26061,13 @@ var DashboardManager = class {
25005
26061
  */
25006
26062
  deleteDashboard(dashboardId) {
25007
26063
  const dashboardPath = this.getDashboardPath(dashboardId);
25008
- const dashboardDir = path9.dirname(dashboardPath);
25009
- if (!fs9.existsSync(dashboardPath)) {
26064
+ const dashboardDir = path13.dirname(dashboardPath);
26065
+ if (!fs13.existsSync(dashboardPath)) {
25010
26066
  logger.warn(`Dashboard not found for deletion: ${dashboardId}`);
25011
26067
  return false;
25012
26068
  }
25013
26069
  try {
25014
- fs9.rmSync(dashboardDir, { recursive: true, force: true });
26070
+ fs13.rmSync(dashboardDir, { recursive: true, force: true });
25015
26071
  logger.info(`Dashboard deleted: ${dashboardId}`);
25016
26072
  return true;
25017
26073
  } catch (error) {
@@ -25026,21 +26082,21 @@ var DashboardManager = class {
25026
26082
  */
25027
26083
  dashboardExists(dashboardId) {
25028
26084
  const dashboardPath = this.getDashboardPath(dashboardId);
25029
- return fs9.existsSync(dashboardPath);
26085
+ return fs13.existsSync(dashboardPath);
25030
26086
  }
25031
26087
  /**
25032
26088
  * Get dashboard count
25033
26089
  * @returns Number of dashboards
25034
26090
  */
25035
26091
  getDashboardCount() {
25036
- if (!fs9.existsSync(this.dashboardsBasePath)) {
26092
+ if (!fs13.existsSync(this.dashboardsBasePath)) {
25037
26093
  return 0;
25038
26094
  }
25039
26095
  try {
25040
- const dashboardDirs = fs9.readdirSync(this.dashboardsBasePath);
26096
+ const dashboardDirs = fs13.readdirSync(this.dashboardsBasePath);
25041
26097
  return dashboardDirs.filter((dir) => {
25042
26098
  const dashboardPath = this.getDashboardPath(dir);
25043
- return fs9.existsSync(dashboardPath);
26099
+ return fs13.existsSync(dashboardPath);
25044
26100
  }).length;
25045
26101
  } catch (error) {
25046
26102
  logger.error("Failed to get dashboard count:", error);
@@ -25050,8 +26106,8 @@ var DashboardManager = class {
25050
26106
  };
25051
26107
 
25052
26108
  // src/reports/report-manager.ts
25053
- import fs10 from "fs";
25054
- import path10 from "path";
26109
+ import fs14 from "fs";
26110
+ import path14 from "path";
25055
26111
  import os4 from "os";
25056
26112
  var ReportManager = class {
25057
26113
  /**
@@ -25060,7 +26116,7 @@ var ReportManager = class {
25060
26116
  */
25061
26117
  constructor(projectId = "snowflake-dataset") {
25062
26118
  this.projectId = projectId;
25063
- this.reportsBasePath = path10.join(
26119
+ this.reportsBasePath = path14.join(
25064
26120
  os4.homedir(),
25065
26121
  ".superatom",
25066
26122
  "projects",
@@ -25074,7 +26130,7 @@ var ReportManager = class {
25074
26130
  * @returns Full path to report data.json file
25075
26131
  */
25076
26132
  getReportPath(reportId) {
25077
- return path10.join(this.reportsBasePath, reportId, "data.json");
26133
+ return path14.join(this.reportsBasePath, reportId, "data.json");
25078
26134
  }
25079
26135
  /**
25080
26136
  * Create a new report
@@ -25084,13 +26140,13 @@ var ReportManager = class {
25084
26140
  */
25085
26141
  createReport(reportId, report) {
25086
26142
  const reportPath = this.getReportPath(reportId);
25087
- const reportDir = path10.dirname(reportPath);
25088
- if (fs10.existsSync(reportPath)) {
26143
+ const reportDir = path14.dirname(reportPath);
26144
+ if (fs14.existsSync(reportPath)) {
25089
26145
  throw new Error(`Report '${reportId}' already exists`);
25090
26146
  }
25091
26147
  const validated = DSLRendererPropsSchema2.parse(report);
25092
- fs10.mkdirSync(reportDir, { recursive: true });
25093
- fs10.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
26148
+ fs14.mkdirSync(reportDir, { recursive: true });
26149
+ fs14.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
25094
26150
  logger.info(`Report created: ${reportId}`);
25095
26151
  return validated;
25096
26152
  }
@@ -25101,12 +26157,12 @@ var ReportManager = class {
25101
26157
  */
25102
26158
  getReport(reportId) {
25103
26159
  const reportPath = this.getReportPath(reportId);
25104
- if (!fs10.existsSync(reportPath)) {
26160
+ if (!fs14.existsSync(reportPath)) {
25105
26161
  logger.warn(`Report not found: ${reportId}`);
25106
26162
  return null;
25107
26163
  }
25108
26164
  try {
25109
- const fileContent = fs10.readFileSync(reportPath, "utf-8");
26165
+ const fileContent = fs14.readFileSync(reportPath, "utf-8");
25110
26166
  const report = JSON.parse(fileContent);
25111
26167
  const validated = DSLRendererPropsSchema2.parse(report);
25112
26168
  return validated;
@@ -25120,16 +26176,16 @@ var ReportManager = class {
25120
26176
  * @returns Array of report objects with their IDs
25121
26177
  */
25122
26178
  getAllReports() {
25123
- if (!fs10.existsSync(this.reportsBasePath)) {
25124
- fs10.mkdirSync(this.reportsBasePath, { recursive: true });
26179
+ if (!fs14.existsSync(this.reportsBasePath)) {
26180
+ fs14.mkdirSync(this.reportsBasePath, { recursive: true });
25125
26181
  return [];
25126
26182
  }
25127
26183
  const reports = [];
25128
26184
  try {
25129
- const reportDirs = fs10.readdirSync(this.reportsBasePath);
26185
+ const reportDirs = fs14.readdirSync(this.reportsBasePath);
25130
26186
  for (const reportId of reportDirs) {
25131
26187
  const reportPath = this.getReportPath(reportId);
25132
- if (fs10.existsSync(reportPath)) {
26188
+ if (fs14.existsSync(reportPath)) {
25133
26189
  const report = this.getReport(reportId);
25134
26190
  if (report) {
25135
26191
  reports.push({ reportId, report });
@@ -25151,13 +26207,13 @@ var ReportManager = class {
25151
26207
  */
25152
26208
  updateReport(reportId, report) {
25153
26209
  const reportPath = this.getReportPath(reportId);
25154
- if (!fs10.existsSync(reportPath)) {
26210
+ if (!fs14.existsSync(reportPath)) {
25155
26211
  logger.warn(`Report not found for update: ${reportId}`);
25156
26212
  return null;
25157
26213
  }
25158
26214
  try {
25159
26215
  const validated = DSLRendererPropsSchema2.parse(report);
25160
- fs10.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
26216
+ fs14.writeFileSync(reportPath, JSON.stringify(validated, null, 4));
25161
26217
  logger.info(`Report updated: ${reportId}`);
25162
26218
  return validated;
25163
26219
  } catch (error) {
@@ -25172,13 +26228,13 @@ var ReportManager = class {
25172
26228
  */
25173
26229
  deleteReport(reportId) {
25174
26230
  const reportPath = this.getReportPath(reportId);
25175
- const reportDir = path10.dirname(reportPath);
25176
- if (!fs10.existsSync(reportPath)) {
26231
+ const reportDir = path14.dirname(reportPath);
26232
+ if (!fs14.existsSync(reportPath)) {
25177
26233
  logger.warn(`Report not found for deletion: ${reportId}`);
25178
26234
  return false;
25179
26235
  }
25180
26236
  try {
25181
- fs10.rmSync(reportDir, { recursive: true, force: true });
26237
+ fs14.rmSync(reportDir, { recursive: true, force: true });
25182
26238
  logger.info(`Report deleted: ${reportId}`);
25183
26239
  return true;
25184
26240
  } catch (error) {
@@ -25193,21 +26249,21 @@ var ReportManager = class {
25193
26249
  */
25194
26250
  reportExists(reportId) {
25195
26251
  const reportPath = this.getReportPath(reportId);
25196
- return fs10.existsSync(reportPath);
26252
+ return fs14.existsSync(reportPath);
25197
26253
  }
25198
26254
  /**
25199
26255
  * Get report count
25200
26256
  * @returns Number of reports
25201
26257
  */
25202
26258
  getReportCount() {
25203
- if (!fs10.existsSync(this.reportsBasePath)) {
26259
+ if (!fs14.existsSync(this.reportsBasePath)) {
25204
26260
  return 0;
25205
26261
  }
25206
26262
  try {
25207
- const reportDirs = fs10.readdirSync(this.reportsBasePath);
26263
+ const reportDirs = fs14.readdirSync(this.reportsBasePath);
25208
26264
  return reportDirs.filter((dir) => {
25209
26265
  const reportPath = this.getReportPath(dir);
25210
- return fs10.existsSync(reportPath);
26266
+ return fs14.existsSync(reportPath);
25211
26267
  }).length;
25212
26268
  } catch (error) {
25213
26269
  logger.error("Failed to get report count:", error);
@@ -25580,8 +26636,8 @@ var CleanupService = class _CleanupService {
25580
26636
  };
25581
26637
 
25582
26638
  // src/dashboardAgent/collection.ts
25583
- import path11 from "path";
25584
- import fs11 from "fs";
26639
+ import path15 from "path";
26640
+ import fs15 from "fs";
25585
26641
 
25586
26642
  // src/dashboardAgent/tools.ts
25587
26643
  import { Type } from "typebox";
@@ -25788,10 +26844,10 @@ function buildDashboardAgentTools(sdk, signal, trace) {
25788
26844
  }
25789
26845
 
25790
26846
  // src/dashboardAgent/collection.ts
25791
- var dynamicImport = new Function("specifier", "return import(specifier)");
26847
+ var dynamicImport2 = new Function("specifier", "return import(specifier)");
25792
26848
  var piModulePromise = null;
25793
26849
  function loadPi() {
25794
- if (!piModulePromise) piModulePromise = dynamicImport("@earendil-works/pi-coding-agent");
26850
+ if (!piModulePromise) piModulePromise = dynamicImport2("@earendil-works/pi-coding-agent");
25795
26851
  return piModulePromise;
25796
26852
  }
25797
26853
  var authAndRegistry = null;
@@ -25909,17 +26965,17 @@ function textDelta(event) {
25909
26965
  return null;
25910
26966
  }
25911
26967
  var runningDashboards = /* @__PURE__ */ new Set();
25912
- function registerDashboardAgentCollection(sdk, config = {}) {
25913
- const piCwd = config.cwd || process.env.PI_AGENT_DIR || path11.join(process.cwd(), ".prompts", "dashboard-agent");
25914
- const runtimeDir = config.runtimeDir || process.env.PI_AGENT_RUNTIME_DIR || path11.join(process.cwd(), ".pi-dashboard-agent-runtime");
25915
- const freshSession = config.freshSession ?? process.env.PI_AGENT_FRESH_SESSION !== "false";
25916
- const SESSIONS_FILE = path11.join(runtimeDir, ".pi-dashboard-sessions.json");
25917
- const DRAFTS_DIR = path11.join(runtimeDir, "drafts");
25918
- const DASHBOARD_LOG_FILE = path11.join(runtimeDir, "dashboard.log");
26968
+ function registerDashboardAgentCollection(sdk, config2 = {}) {
26969
+ const piCwd = config2.cwd || process.env.PI_AGENT_DIR || path15.join(process.cwd(), ".prompts", "dashboard-agent");
26970
+ const runtimeDir = config2.runtimeDir || process.env.PI_AGENT_RUNTIME_DIR || path15.join(process.cwd(), ".pi-dashboard-agent-runtime");
26971
+ const freshSession = config2.freshSession ?? process.env.PI_AGENT_FRESH_SESSION !== "false";
26972
+ const SESSIONS_FILE = path15.join(runtimeDir, ".pi-dashboard-sessions.json");
26973
+ const DRAFTS_DIR = path15.join(runtimeDir, "drafts");
26974
+ const DASHBOARD_LOG_FILE = path15.join(runtimeDir, "dashboard.log");
25919
26975
  function appendLog(text) {
25920
26976
  try {
25921
- fs11.mkdirSync(runtimeDir, { recursive: true });
25922
- fs11.appendFileSync(DASHBOARD_LOG_FILE, text);
26977
+ fs15.mkdirSync(runtimeDir, { recursive: true });
26978
+ fs15.appendFileSync(DASHBOARD_LOG_FILE, text);
25923
26979
  } catch (err) {
25924
26980
  console.warn(`[DashboardAgent] Failed to write dashboard.log: ${err?.message ?? err}`);
25925
26981
  }
@@ -25931,15 +26987,15 @@ ${fullPrompt}
25931
26987
  --- Response ---
25932
26988
  `;
25933
26989
  try {
25934
- fs11.mkdirSync(runtimeDir, { recursive: true });
25935
- fs11.writeFileSync(DASHBOARD_LOG_FILE, header);
26990
+ fs15.mkdirSync(runtimeDir, { recursive: true });
26991
+ fs15.writeFileSync(DASHBOARD_LOG_FILE, header);
25936
26992
  } catch (err) {
25937
26993
  console.warn(`[DashboardAgent] Failed to write dashboard.log: ${err?.message ?? err}`);
25938
26994
  }
25939
26995
  }
25940
26996
  function readDraft(dashboardId) {
25941
26997
  try {
25942
- const raw = fs11.readFileSync(path11.join(DRAFTS_DIR, `${dashboardId}.json`), "utf-8");
26998
+ const raw = fs15.readFileSync(path15.join(DRAFTS_DIR, `${dashboardId}.json`), "utf-8");
25943
26999
  return JSON.parse(raw);
25944
27000
  } catch (err) {
25945
27001
  console.warn(`[DashboardAgent] Failed to read draft for ${dashboardId}: ${err?.message ?? err}`);
@@ -25948,7 +27004,7 @@ ${fullPrompt}
25948
27004
  }
25949
27005
  function loadSessionMap() {
25950
27006
  try {
25951
- return JSON.parse(fs11.readFileSync(SESSIONS_FILE, "utf-8"));
27007
+ return JSON.parse(fs15.readFileSync(SESSIONS_FILE, "utf-8"));
25952
27008
  } catch {
25953
27009
  return {};
25954
27010
  }
@@ -25957,8 +27013,8 @@ ${fullPrompt}
25957
27013
  const map = loadSessionMap();
25958
27014
  map[dashboardId] = sessionFile;
25959
27015
  try {
25960
- fs11.mkdirSync(piCwd, { recursive: true });
25961
- fs11.writeFileSync(SESSIONS_FILE, JSON.stringify(map, null, 2));
27016
+ fs15.mkdirSync(piCwd, { recursive: true });
27017
+ fs15.writeFileSync(SESSIONS_FILE, JSON.stringify(map, null, 2));
25962
27018
  } catch (err) {
25963
27019
  console.warn(`[DashboardAgent] Failed to persist session file for ${dashboardId}: ${err?.message ?? err}`);
25964
27020
  }
@@ -26030,8 +27086,8 @@ ${fullPrompt}
26030
27086
  const { createAgentSession, SessionManager } = await loadPi();
26031
27087
  const { authStorage, modelRegistry } = await getAuthAndRegistry();
26032
27088
  let model;
26033
- const explicitProvider = config.provider || process.env.PI_AGENT_PROVIDER;
26034
- const explicitModel = config.model || process.env.PI_AGENT_MODEL;
27089
+ const explicitProvider = config2.provider || process.env.PI_AGENT_PROVIDER;
27090
+ const explicitModel = config2.model || process.env.PI_AGENT_MODEL;
26035
27091
  if (explicitProvider || explicitModel) {
26036
27092
  const providerName = explicitProvider || "openrouter";
26037
27093
  const modelId = explicitModel || "anthropic/claude-sonnet-4.5";
@@ -26134,7 +27190,7 @@ ${turn.responseText ?? ""}`).join("\n\n");
26134
27190
  console.warn(`[DashboardAgent] Failed to fetch previous responses for ${dashboardId}: ${err?.message ?? err}`);
26135
27191
  }
26136
27192
  }
26137
- const draftPath = path11.join(DRAFTS_DIR, `${dashboardId}.json`);
27193
+ const draftPath = path15.join(DRAFTS_DIR, `${dashboardId}.json`);
26138
27194
  const fullPrompt = [
26139
27195
  `Dashboard ID: ${dashboardId}.`,
26140
27196
  projectId ? `Project ID: ${projectId}.` : null,
@@ -26166,7 +27222,7 @@ ${previousResponseText}` : null,
26166
27222
  let sessionManager;
26167
27223
  if (!freshSession) {
26168
27224
  const existingSessionFile = loadSessionMap()[dashboardId];
26169
- if (existingSessionFile && fs11.existsSync(existingSessionFile)) {
27225
+ if (existingSessionFile && fs15.existsSync(existingSessionFile)) {
26170
27226
  sessionManager = SessionManager.open(existingSessionFile, runtimeDir);
26171
27227
  console.log(`[DashboardAgent] Resuming session file ${existingSessionFile} for dashboard ${dashboardId}`);
26172
27228
  } else {
@@ -26247,7 +27303,7 @@ ${description}
26247
27303
  };
26248
27304
  signal?.addEventListener("abort", onAbort, { once: true });
26249
27305
  try {
26250
- fs11.unlinkSync(draftPath);
27306
+ fs15.unlinkSync(draftPath);
26251
27307
  } catch {
26252
27308
  }
26253
27309
  let result;
@@ -26267,7 +27323,7 @@ ${description}
26267
27323
  signal?.removeEventListener("abort", onAbort);
26268
27324
  if (freshSession && session.sessionFile) {
26269
27325
  try {
26270
- fs11.unlinkSync(session.sessionFile);
27326
+ fs15.unlinkSync(session.sessionFile);
26271
27327
  } catch (err) {
26272
27328
  console.warn(`[DashboardAgent] Failed to delete fresh-session file ${session.sessionFile}: ${err?.message ?? err}`);
26273
27329
  }
@@ -26399,7 +27455,7 @@ ${description}
26399
27455
  // src/index.ts
26400
27456
  var DEFAULT_WS_URL = "wss://ws.superatom.ai/websocket";
26401
27457
  var SuperatomSDK = class {
26402
- constructor(config) {
27458
+ constructor(config2) {
26403
27459
  this.ws = null;
26404
27460
  this.messageHandlers = /* @__PURE__ */ new Map();
26405
27461
  this.messageTypeHandlers = /* @__PURE__ */ new Map();
@@ -26426,39 +27482,39 @@ var SuperatomSDK = class {
26426
27482
  // since an outage of unknown length shouldn't grow this without limit.
26427
27483
  this.pendingOutbox = [];
26428
27484
  this.MAX_OUTBOX_SIZE = 50;
26429
- if (config.logLevel) {
26430
- logger.setLogLevel(config.logLevel);
26431
- }
26432
- this.apiKey = config.apiKey;
26433
- this.projectId = config.projectId;
26434
- this.type = config.type || "data-agent";
26435
- this.bundleDir = config.bundleDir;
26436
- this.url = config.url || process.env.SA_WEBSOCKET_URL || DEFAULT_WS_URL;
26437
- this.anthropicApiKey = config.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || "";
26438
- this.groqApiKey = config.GROQ_API_KEY || process.env.GROQ_API_KEY || "";
26439
- this.geminiApiKey = config.GEMINI_API_KEY || process.env.GEMINI_API_KEY || "";
26440
- this.openaiApiKey = config.OPENAI_API_KEY || process.env.OPENAI_API_KEY || "";
26441
- this.llmProviders = config.LLM_PROVIDERS || getLLMProviders();
26442
- this.databaseType = config.databaseType || "postgresql";
26443
- setFmtConfig({ locale: config.locale, currency: config.currency });
26444
- this.modelStrategy = config.modelStrategy || "fast";
26445
- this.mainAgentModel = config.mainAgentModel || "anthropic/claude-sonnet-4-5-20250929";
26446
- this.sourceAgentModel = config.sourceAgentModel || "anthropic/claude-sonnet-4-5-20250929";
26447
- this.dashCompModels = config.dashCompModels;
26448
- this.conversationSimilarityThreshold = config.conversationSimilarityThreshold ?? 0.8;
27485
+ if (config2.logLevel) {
27486
+ logger.setLogLevel(config2.logLevel);
27487
+ }
27488
+ this.apiKey = config2.apiKey;
27489
+ this.projectId = config2.projectId;
27490
+ this.type = config2.type || "data-agent";
27491
+ this.bundleDir = config2.bundleDir;
27492
+ this.url = config2.url || process.env.SA_WEBSOCKET_URL || DEFAULT_WS_URL;
27493
+ this.anthropicApiKey = config2.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || "";
27494
+ this.groqApiKey = config2.GROQ_API_KEY || process.env.GROQ_API_KEY || "";
27495
+ this.geminiApiKey = config2.GEMINI_API_KEY || process.env.GEMINI_API_KEY || "";
27496
+ this.openaiApiKey = config2.OPENAI_API_KEY || process.env.OPENAI_API_KEY || "";
27497
+ this.llmProviders = config2.LLM_PROVIDERS || getLLMProviders();
27498
+ this.databaseType = config2.databaseType || "postgresql";
27499
+ setFmtConfig({ locale: config2.locale, currency: config2.currency });
27500
+ this.modelStrategy = config2.modelStrategy || "fast";
27501
+ this.mainAgentModel = config2.mainAgentModel || "anthropic/claude-sonnet-4-5-20250929";
27502
+ this.sourceAgentModel = config2.sourceAgentModel || "anthropic/claude-sonnet-4-5-20250929";
27503
+ this.dashCompModels = config2.dashCompModels;
27504
+ this.conversationSimilarityThreshold = config2.conversationSimilarityThreshold ?? 0.8;
26449
27505
  this.applyModelStrategy(this.modelStrategy);
26450
27506
  this.applyConversationSimilarityThreshold(this.conversationSimilarityThreshold);
26451
- if (config.queryCacheTTL !== void 0) {
26452
- queryCache.setTTL(config.queryCacheTTL);
27507
+ if (config2.queryCacheTTL !== void 0) {
27508
+ queryCache.setTTL(config2.queryCacheTTL);
26453
27509
  }
26454
- if (config.dashboardHistoryTTL !== void 0) {
26455
- dashboardConversationHistory.setTTL(config.dashboardHistoryTTL);
27510
+ if (config2.dashboardHistoryTTL !== void 0) {
27511
+ dashboardConversationHistory.setTTL(config2.dashboardHistoryTTL);
26456
27512
  }
26457
27513
  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`);
26458
27514
  this.userManager = new UserManager(this.projectId, 5e3);
26459
27515
  this.dashboardManager = new DashboardManager(this.projectId);
26460
27516
  this.reportManager = new ReportManager(this.projectId);
26461
- this.initializePromptLoader(config.promptsDir).catch((error) => {
27517
+ this.initializePromptLoader(config2.promptsDir).catch((error) => {
26462
27518
  logger.error("Failed to initialize PromptLoader:", error);
26463
27519
  });
26464
27520
  this.initializeUserManager().catch((error) => {
@@ -26687,6 +27743,14 @@ var SuperatomSDK = class {
26687
27743
  case "DASHBOARD_AGENT_ABORT_REQ":
26688
27744
  handleDashboardAgentAbort(parsed, (msg) => this.send(msg));
26689
27745
  break;
27746
+ case "CODING_AGENT_REQ":
27747
+ handleCodingAgentRequest(parsed, this.collections, (msg) => this.send(msg)).catch((error) => {
27748
+ logger.error("Failed to handle coding agent request:", error);
27749
+ });
27750
+ break;
27751
+ case "CODING_AGENT_ABORT_REQ":
27752
+ handleCodingAgentAbort(parsed, (msg) => this.send(msg));
27753
+ break;
26690
27754
  case "REPORT_COMP_REQ":
26691
27755
  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) => {
26692
27756
  logger.error("Failed to handle report comp request:", error);
@@ -27024,20 +28088,35 @@ export {
27024
28088
  UILogCollector,
27025
28089
  UserManager,
27026
28090
  anthropicLLM,
28091
+ assertReadOnlySql,
28092
+ createCodexSession,
28093
+ createCodingAgentSession,
27027
28094
  dashboardConversationHistory,
28095
+ describeTriggers,
28096
+ emitAgentEvent,
27028
28097
  geminiLLM,
28098
+ getCodingAgent,
28099
+ getCodingAgentsConfig,
27029
28100
  groqLLM,
27030
28101
  hybridRerank,
28102
+ listCodingAgents,
27031
28103
  llmUsageLogger,
27032
28104
  logger,
27033
28105
  normalizeScriptBody,
28106
+ onAgentRun,
27034
28107
  openaiLLM,
27035
28108
  queryCache,
28109
+ registerCodingAgentCollection,
28110
+ registerCodingAgents,
27036
28111
  registerDashboardAgentCollection,
27037
28112
  rerankChromaResults,
27038
28113
  rerankConversationResults,
27039
28114
  resolveScriptRecipeStore,
28115
+ runCodingAgent,
27040
28116
  runScript,
27041
- userPromptErrorLogger
28117
+ startAgentTriggers,
28118
+ stopAgentTriggers,
28119
+ userPromptErrorLogger,
28120
+ writeBridgeShims
27042
28121
  };
27043
28122
  //# sourceMappingURL=index.mjs.map