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