@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/README.md +942 -942
- package/dist/index.d.mts +611 -1
- package/dist/index.d.ts +611 -1
- package/dist/index.js +1193 -99
- package/dist/index.mjs +1253 -174
- package/package.json +4 -1
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(
|
|
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 =
|
|
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,
|
|
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 =
|
|
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,
|
|
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 =
|
|
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,
|
|
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(`[${
|
|
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(`[${
|
|
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 =
|
|
13353
|
+
const maxLimit = config2.maxLimit ?? MAX_AGENT_QUERY_LIMIT;
|
|
13323
13354
|
if (typeof cleanedQuery === "string") {
|
|
13324
|
-
const limitedQuery = ensureQueryLimit(cleanedQuery,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
13378
|
+
const { query } = validateAndCleanQuery(cleanedProps.query, config2);
|
|
13348
13379
|
cleanedProps.query = query;
|
|
13349
13380
|
}
|
|
13350
13381
|
if (cleanedProps.query && cleanedProps.externalTool) {
|
|
13351
|
-
logger.info(`[${
|
|
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
|
|
13955
|
+
const slug2 = this.toSlug(name);
|
|
13925
13956
|
if (this.store) {
|
|
13926
13957
|
try {
|
|
13927
|
-
const taken = await this.store.fileBaseTaken(
|
|
13928
|
-
if (taken) return `${
|
|
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
|
|
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(
|
|
15322
|
-
this.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(
|
|
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 =
|
|
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(
|
|
15873
|
-
this.model =
|
|
15874
|
-
this.fastModel =
|
|
15875
|
-
this.defaultLimit =
|
|
15876
|
-
this.apiKey =
|
|
15877
|
-
this.modelStrategy =
|
|
15878
|
-
this.conversationSimilarityThreshold =
|
|
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(
|
|
17068
|
-
super(
|
|
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(
|
|
17090
|
-
super(
|
|
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(
|
|
17112
|
-
super(
|
|
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(
|
|
17134
|
-
super(
|
|
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
|
|
24101
|
-
var
|
|
24102
|
-
var
|
|
24103
|
-
function
|
|
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
|
-
|
|
24106
|
-
for (const [key, ts] of
|
|
24107
|
-
if (now - ts >
|
|
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
|
|
24111
|
-
const ts =
|
|
25166
|
+
function consumeAbortTombstone4(reportId) {
|
|
25167
|
+
const ts = recentlyAborted4.get(reportId);
|
|
24112
25168
|
if (ts === void 0) return false;
|
|
24113
|
-
|
|
24114
|
-
return Date.now() - ts <=
|
|
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 =
|
|
25181
|
+
const controller = inFlightAborts4.get(reportId);
|
|
24126
25182
|
if (controller) {
|
|
24127
25183
|
controller.abort();
|
|
24128
|
-
|
|
25184
|
+
inFlightAborts4.delete(reportId);
|
|
24129
25185
|
logger.info(`[ReportCompAbort] Aborted in-flight run for report ${reportId}`);
|
|
24130
25186
|
} else {
|
|
24131
|
-
|
|
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 (
|
|
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
|
-
|
|
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)
|
|
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
|
|
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
|
|
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}): ${
|
|
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
|
-
|
|
25546
|
+
asArray2(source.schema?.entities).concat(asArray2(source.schema?.tables)),
|
|
24491
25547
|
`${source.name} (${source.type})`
|
|
24492
25548
|
);
|
|
24493
|
-
renderRelationships(
|
|
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(
|
|
24502
|
-
renderRelationships(
|
|
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
|
|
24589
|
-
import
|
|
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 =
|
|
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 =
|
|
24628
|
-
if (!
|
|
25683
|
+
const dir = path12.dirname(this.filePath);
|
|
25684
|
+
if (!fs12.existsSync(dir)) {
|
|
24629
25685
|
logger.info(`Creating directory structure: ${dir}`);
|
|
24630
|
-
|
|
25686
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
24631
25687
|
}
|
|
24632
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
24660
|
-
if (!
|
|
24661
|
-
|
|
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
|
-
|
|
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
|
|
24887
|
-
import
|
|
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 =
|
|
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
|
|
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 =
|
|
24921
|
-
if (
|
|
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
|
-
|
|
24926
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
24957
|
-
|
|
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 =
|
|
26018
|
+
const dashboardDirs = fs13.readdirSync(this.dashboardsBasePath);
|
|
24963
26019
|
for (const dashboardId of dashboardDirs) {
|
|
24964
26020
|
const dashboardPath = this.getDashboardPath(dashboardId);
|
|
24965
|
-
if (
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
25009
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
26092
|
+
if (!fs13.existsSync(this.dashboardsBasePath)) {
|
|
25037
26093
|
return 0;
|
|
25038
26094
|
}
|
|
25039
26095
|
try {
|
|
25040
|
-
const dashboardDirs =
|
|
26096
|
+
const dashboardDirs = fs13.readdirSync(this.dashboardsBasePath);
|
|
25041
26097
|
return dashboardDirs.filter((dir) => {
|
|
25042
26098
|
const dashboardPath = this.getDashboardPath(dir);
|
|
25043
|
-
return
|
|
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
|
|
25054
|
-
import
|
|
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 =
|
|
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
|
|
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 =
|
|
25088
|
-
if (
|
|
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
|
-
|
|
25093
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
25124
|
-
|
|
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 =
|
|
26185
|
+
const reportDirs = fs14.readdirSync(this.reportsBasePath);
|
|
25130
26186
|
for (const reportId of reportDirs) {
|
|
25131
26187
|
const reportPath = this.getReportPath(reportId);
|
|
25132
|
-
if (
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
25176
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
26259
|
+
if (!fs14.existsSync(this.reportsBasePath)) {
|
|
25204
26260
|
return 0;
|
|
25205
26261
|
}
|
|
25206
26262
|
try {
|
|
25207
|
-
const reportDirs =
|
|
26263
|
+
const reportDirs = fs14.readdirSync(this.reportsBasePath);
|
|
25208
26264
|
return reportDirs.filter((dir) => {
|
|
25209
26265
|
const reportPath = this.getReportPath(dir);
|
|
25210
|
-
return
|
|
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
|
|
25584
|
-
import
|
|
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
|
|
26847
|
+
var dynamicImport2 = new Function("specifier", "return import(specifier)");
|
|
25792
26848
|
var piModulePromise = null;
|
|
25793
26849
|
function loadPi() {
|
|
25794
|
-
if (!piModulePromise) piModulePromise =
|
|
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,
|
|
25913
|
-
const piCwd =
|
|
25914
|
-
const runtimeDir =
|
|
25915
|
-
const freshSession =
|
|
25916
|
-
const SESSIONS_FILE =
|
|
25917
|
-
const DRAFTS_DIR =
|
|
25918
|
-
const DASHBOARD_LOG_FILE =
|
|
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
|
-
|
|
25922
|
-
|
|
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
|
-
|
|
25935
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
25961
|
-
|
|
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 =
|
|
26034
|
-
const explicitModel =
|
|
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 =
|
|
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 &&
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
26430
|
-
logger.setLogLevel(
|
|
26431
|
-
}
|
|
26432
|
-
this.apiKey =
|
|
26433
|
-
this.projectId =
|
|
26434
|
-
this.type =
|
|
26435
|
-
this.bundleDir =
|
|
26436
|
-
this.url =
|
|
26437
|
-
this.anthropicApiKey =
|
|
26438
|
-
this.groqApiKey =
|
|
26439
|
-
this.geminiApiKey =
|
|
26440
|
-
this.openaiApiKey =
|
|
26441
|
-
this.llmProviders =
|
|
26442
|
-
this.databaseType =
|
|
26443
|
-
setFmtConfig({ locale:
|
|
26444
|
-
this.modelStrategy =
|
|
26445
|
-
this.mainAgentModel =
|
|
26446
|
-
this.sourceAgentModel =
|
|
26447
|
-
this.dashCompModels =
|
|
26448
|
-
this.conversationSimilarityThreshold =
|
|
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 (
|
|
26452
|
-
queryCache.setTTL(
|
|
27507
|
+
if (config2.queryCacheTTL !== void 0) {
|
|
27508
|
+
queryCache.setTTL(config2.queryCacheTTL);
|
|
26453
27509
|
}
|
|
26454
|
-
if (
|
|
26455
|
-
dashboardConversationHistory.setTTL(
|
|
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(
|
|
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
|
-
|
|
28117
|
+
startAgentTriggers,
|
|
28118
|
+
stopAgentTriggers,
|
|
28119
|
+
userPromptErrorLogger,
|
|
28120
|
+
writeBridgeShims
|
|
27042
28121
|
};
|
|
27043
28122
|
//# sourceMappingURL=index.mjs.map
|