@qodo/sdk 0.1.0
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/LICENSE +118 -0
- package/README.md +121 -0
- package/dist/api/agent.d.ts +69 -0
- package/dist/api/agent.d.ts.map +1 -0
- package/dist/api/agent.js +1034 -0
- package/dist/api/agent.js.map +1 -0
- package/dist/api/analytics.d.ts +43 -0
- package/dist/api/analytics.d.ts.map +1 -0
- package/dist/api/analytics.js +163 -0
- package/dist/api/analytics.js.map +1 -0
- package/dist/api/http.d.ts +5 -0
- package/dist/api/http.d.ts.map +1 -0
- package/dist/api/http.js +59 -0
- package/dist/api/http.js.map +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +17 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/taskTracking.d.ts +54 -0
- package/dist/api/taskTracking.d.ts.map +1 -0
- package/dist/api/taskTracking.js +208 -0
- package/dist/api/taskTracking.js.map +1 -0
- package/dist/api/types.d.ts +92 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/utils.d.ts +8 -0
- package/dist/api/utils.d.ts.map +1 -0
- package/dist/api/utils.js +54 -0
- package/dist/api/utils.js.map +1 -0
- package/dist/api/websocket.d.ts +74 -0
- package/dist/api/websocket.d.ts.map +1 -0
- package/dist/api/websocket.js +685 -0
- package/dist/api/websocket.js.map +1 -0
- package/dist/auth/index.d.ts +25 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +85 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/clients/index.d.ts +8 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +7 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/info/InfoClient.d.ts +37 -0
- package/dist/clients/info/InfoClient.d.ts.map +1 -0
- package/dist/clients/info/InfoClient.js +69 -0
- package/dist/clients/info/InfoClient.js.map +1 -0
- package/dist/clients/info/index.d.ts +4 -0
- package/dist/clients/info/index.d.ts.map +1 -0
- package/dist/clients/info/index.js +2 -0
- package/dist/clients/info/index.js.map +1 -0
- package/dist/clients/info/types.d.ts +21 -0
- package/dist/clients/info/types.d.ts.map +1 -0
- package/dist/clients/info/types.js +2 -0
- package/dist/clients/info/types.js.map +1 -0
- package/dist/clients/sessions/SessionsClient.d.ts +34 -0
- package/dist/clients/sessions/SessionsClient.d.ts.map +1 -0
- package/dist/clients/sessions/SessionsClient.js +71 -0
- package/dist/clients/sessions/SessionsClient.js.map +1 -0
- package/dist/clients/sessions/index.d.ts +4 -0
- package/dist/clients/sessions/index.d.ts.map +1 -0
- package/dist/clients/sessions/index.js +2 -0
- package/dist/clients/sessions/index.js.map +1 -0
- package/dist/clients/sessions/types.d.ts +20 -0
- package/dist/clients/sessions/types.d.ts.map +1 -0
- package/dist/clients/sessions/types.js +2 -0
- package/dist/clients/sessions/types.js.map +1 -0
- package/dist/config/ConfigManager.d.ts +43 -0
- package/dist/config/ConfigManager.d.ts.map +1 -0
- package/dist/config/ConfigManager.js +472 -0
- package/dist/config/ConfigManager.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +7 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/urlConfig.d.ts +15 -0
- package/dist/config/urlConfig.d.ts.map +1 -0
- package/dist/config/urlConfig.js +75 -0
- package/dist/config/urlConfig.js.map +1 -0
- package/dist/constants/errors.d.ts +2 -0
- package/dist/constants/errors.d.ts.map +1 -0
- package/dist/constants/errors.js +2 -0
- package/dist/constants/errors.js.map +1 -0
- package/dist/constants/index.d.ts +7 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +11 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/tools.d.ts +4 -0
- package/dist/constants/tools.d.ts.map +1 -0
- package/dist/constants/tools.js +4 -0
- package/dist/constants/tools.js.map +1 -0
- package/dist/constants/versions.d.ts +2 -0
- package/dist/constants/versions.d.ts.map +1 -0
- package/dist/constants/versions.js +2 -0
- package/dist/constants/versions.js.map +1 -0
- package/dist/context/buildUserContext.d.ts +18 -0
- package/dist/context/buildUserContext.d.ts.map +1 -0
- package/dist/context/buildUserContext.js +34 -0
- package/dist/context/buildUserContext.js.map +1 -0
- package/dist/context/index.d.ts +9 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +9 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/messageManager.d.ts +42 -0
- package/dist/context/messageManager.d.ts.map +1 -0
- package/dist/context/messageManager.js +322 -0
- package/dist/context/messageManager.js.map +1 -0
- package/dist/context/taskFocus.d.ts +2 -0
- package/dist/context/taskFocus.d.ts.map +1 -0
- package/dist/context/taskFocus.js +26 -0
- package/dist/context/taskFocus.js.map +1 -0
- package/dist/context/userInput.d.ts +3 -0
- package/dist/context/userInput.d.ts.map +1 -0
- package/dist/context/userInput.js +20 -0
- package/dist/context/userInput.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/MCPManager.d.ts +125 -0
- package/dist/mcp/MCPManager.d.ts.map +1 -0
- package/dist/mcp/MCPManager.js +616 -0
- package/dist/mcp/MCPManager.js.map +1 -0
- package/dist/mcp/approvedTools.d.ts +4 -0
- package/dist/mcp/approvedTools.d.ts.map +1 -0
- package/dist/mcp/approvedTools.js +19 -0
- package/dist/mcp/approvedTools.js.map +1 -0
- package/dist/mcp/baseServer.d.ts +75 -0
- package/dist/mcp/baseServer.d.ts.map +1 -0
- package/dist/mcp/baseServer.js +107 -0
- package/dist/mcp/baseServer.js.map +1 -0
- package/dist/mcp/builtinServers.d.ts +15 -0
- package/dist/mcp/builtinServers.d.ts.map +1 -0
- package/dist/mcp/builtinServers.js +155 -0
- package/dist/mcp/builtinServers.js.map +1 -0
- package/dist/mcp/dynamicBEServer.d.ts +20 -0
- package/dist/mcp/dynamicBEServer.d.ts.map +1 -0
- package/dist/mcp/dynamicBEServer.js +52 -0
- package/dist/mcp/dynamicBEServer.js.map +1 -0
- package/dist/mcp/index.d.ts +19 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +24 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcpInitialization.d.ts +2 -0
- package/dist/mcp/mcpInitialization.d.ts.map +1 -0
- package/dist/mcp/mcpInitialization.js +56 -0
- package/dist/mcp/mcpInitialization.js.map +1 -0
- package/dist/mcp/servers/filesystem.d.ts +75 -0
- package/dist/mcp/servers/filesystem.d.ts.map +1 -0
- package/dist/mcp/servers/filesystem.js +992 -0
- package/dist/mcp/servers/filesystem.js.map +1 -0
- package/dist/mcp/servers/gerrit.d.ts +19 -0
- package/dist/mcp/servers/gerrit.d.ts.map +1 -0
- package/dist/mcp/servers/gerrit.js +515 -0
- package/dist/mcp/servers/gerrit.js.map +1 -0
- package/dist/mcp/servers/git.d.ts +18 -0
- package/dist/mcp/servers/git.d.ts.map +1 -0
- package/dist/mcp/servers/git.js +441 -0
- package/dist/mcp/servers/git.js.map +1 -0
- package/dist/mcp/servers/ripgrep.d.ts +34 -0
- package/dist/mcp/servers/ripgrep.d.ts.map +1 -0
- package/dist/mcp/servers/ripgrep.js +517 -0
- package/dist/mcp/servers/ripgrep.js.map +1 -0
- package/dist/mcp/servers/shell.d.ts +20 -0
- package/dist/mcp/servers/shell.d.ts.map +1 -0
- package/dist/mcp/servers/shell.js +603 -0
- package/dist/mcp/servers/shell.js.map +1 -0
- package/dist/mcp/serversRegistry.d.ts +55 -0
- package/dist/mcp/serversRegistry.d.ts.map +1 -0
- package/dist/mcp/serversRegistry.js +410 -0
- package/dist/mcp/serversRegistry.js.map +1 -0
- package/dist/mcp/toolProcessor.d.ts +42 -0
- package/dist/mcp/toolProcessor.d.ts.map +1 -0
- package/dist/mcp/toolProcessor.js +200 -0
- package/dist/mcp/toolProcessor.js.map +1 -0
- package/dist/mcp/types.d.ts +29 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +2 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/parser/index.d.ts +72 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +967 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/types.d.ts +153 -0
- package/dist/parser/types.d.ts.map +1 -0
- package/dist/parser/types.js +6 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/parser/utils.d.ts +18 -0
- package/dist/parser/utils.d.ts.map +1 -0
- package/dist/parser/utils.js +64 -0
- package/dist/parser/utils.js.map +1 -0
- package/dist/sdk/QodoSDK.d.ts +152 -0
- package/dist/sdk/QodoSDK.d.ts.map +1 -0
- package/dist/sdk/QodoSDK.js +786 -0
- package/dist/sdk/QodoSDK.js.map +1 -0
- package/dist/sdk/bootstrap.d.ts +16 -0
- package/dist/sdk/bootstrap.d.ts.map +1 -0
- package/dist/sdk/bootstrap.js +21 -0
- package/dist/sdk/bootstrap.js.map +1 -0
- package/dist/sdk/builders.d.ts +54 -0
- package/dist/sdk/builders.d.ts.map +1 -0
- package/dist/sdk/builders.js +117 -0
- package/dist/sdk/builders.js.map +1 -0
- package/dist/sdk/defaults.d.ts +11 -0
- package/dist/sdk/defaults.d.ts.map +1 -0
- package/dist/sdk/defaults.js +39 -0
- package/dist/sdk/defaults.js.map +1 -0
- package/dist/sdk/discovery.d.ts +2 -0
- package/dist/sdk/discovery.d.ts.map +1 -0
- package/dist/sdk/discovery.js +25 -0
- package/dist/sdk/discovery.js.map +1 -0
- package/dist/sdk/events.d.ts +168 -0
- package/dist/sdk/events.d.ts.map +1 -0
- package/dist/sdk/events.js +52 -0
- package/dist/sdk/events.js.map +1 -0
- package/dist/sdk/index.d.ts +17 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +17 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/runner/AgentRunner.d.ts +22 -0
- package/dist/sdk/runner/AgentRunner.d.ts.map +1 -0
- package/dist/sdk/runner/AgentRunner.js +222 -0
- package/dist/sdk/runner/AgentRunner.js.map +1 -0
- package/dist/sdk/runner/finalize.d.ts +9 -0
- package/dist/sdk/runner/finalize.d.ts.map +1 -0
- package/dist/sdk/runner/finalize.js +115 -0
- package/dist/sdk/runner/finalize.js.map +1 -0
- package/dist/sdk/runner/formats.d.ts +7 -0
- package/dist/sdk/runner/formats.d.ts.map +1 -0
- package/dist/sdk/runner/formats.js +91 -0
- package/dist/sdk/runner/formats.js.map +1 -0
- package/dist/sdk/runner/index.d.ts +9 -0
- package/dist/sdk/runner/index.d.ts.map +1 -0
- package/dist/sdk/runner/index.js +9 -0
- package/dist/sdk/runner/index.js.map +1 -0
- package/dist/sdk/runner/progress.d.ts +3 -0
- package/dist/sdk/runner/progress.d.ts.map +1 -0
- package/dist/sdk/runner/progress.js +16 -0
- package/dist/sdk/runner/progress.js.map +1 -0
- package/dist/sdk/schemas.d.ts +50 -0
- package/dist/sdk/schemas.d.ts.map +1 -0
- package/dist/sdk/schemas.js +145 -0
- package/dist/sdk/schemas.js.map +1 -0
- package/dist/session/SessionContext.d.ts +86 -0
- package/dist/session/SessionContext.d.ts.map +1 -0
- package/dist/session/SessionContext.js +395 -0
- package/dist/session/SessionContext.js.map +1 -0
- package/dist/session/environment.d.ts +42 -0
- package/dist/session/environment.d.ts.map +1 -0
- package/dist/session/environment.js +27 -0
- package/dist/session/environment.js.map +1 -0
- package/dist/session/history.d.ts +3 -0
- package/dist/session/history.d.ts.map +1 -0
- package/dist/session/history.js +67 -0
- package/dist/session/history.js.map +1 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +9 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/serverData.d.ts +38 -0
- package/dist/session/serverData.d.ts.map +1 -0
- package/dist/session/serverData.js +241 -0
- package/dist/session/serverData.js.map +1 -0
- package/dist/tracking/Tracker.d.ts +55 -0
- package/dist/tracking/Tracker.d.ts.map +1 -0
- package/dist/tracking/Tracker.js +217 -0
- package/dist/tracking/Tracker.js.map +1 -0
- package/dist/tracking/index.d.ts +8 -0
- package/dist/tracking/index.d.ts.map +1 -0
- package/dist/tracking/index.js +8 -0
- package/dist/tracking/index.js.map +1 -0
- package/dist/tracking/schemas.d.ts +292 -0
- package/dist/tracking/schemas.d.ts.map +1 -0
- package/dist/tracking/schemas.js +91 -0
- package/dist/tracking/schemas.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/extractSetFlags.d.ts +6 -0
- package/dist/utils/extractSetFlags.d.ts.map +1 -0
- package/dist/utils/extractSetFlags.js +16 -0
- package/dist/utils/extractSetFlags.js.map +1 -0
- package/dist/utils/formatTimeAgo.d.ts +2 -0
- package/dist/utils/formatTimeAgo.d.ts.map +1 -0
- package/dist/utils/formatTimeAgo.js +20 -0
- package/dist/utils/formatTimeAgo.js.map +1 -0
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/machineId.d.ts +14 -0
- package/dist/utils/machineId.d.ts.map +1 -0
- package/dist/utils/machineId.js +66 -0
- package/dist/utils/machineId.js.map +1 -0
- package/dist/utils/pathUtils.d.ts +22 -0
- package/dist/utils/pathUtils.d.ts.map +1 -0
- package/dist/utils/pathUtils.js +54 -0
- package/dist/utils/pathUtils.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
import { ConfigManager } from '../config/index.js';
|
|
2
|
+
import { v4 as uuid } from 'uuid';
|
|
3
|
+
import { findNearestAgentConfig } from './discovery.js';
|
|
4
|
+
import { buildDefaultSdkAgentConfig } from './defaults.js';
|
|
5
|
+
import { mcpInitialization, ServerRegistry } from '../mcp/index.js';
|
|
6
|
+
import { runWithEnvironment, ServerData, SessionContext } from '../session/index.js';
|
|
7
|
+
import { MessageManager } from '../context/index.js';
|
|
8
|
+
import { AgentAPI } from '../api/agent.js';
|
|
9
|
+
import { AgentRunnerCore } from './runner/index.js';
|
|
10
|
+
import { BackendBootstrap } from './bootstrap.js';
|
|
11
|
+
import { QodoInfoClient, QodoSessionsClient } from '../clients/index.js';
|
|
12
|
+
import { canonicalizePathSync, extractSetFlags } from '../utils/index.js';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { extractProgress } from './runner/progress.js';
|
|
15
|
+
import { toLangChainDict, toOpenAIChat } from './runner/formats.js';
|
|
16
|
+
import { buildFinalResult } from './runner/finalize.js';
|
|
17
|
+
import { createSdkEvent, getSdkVersion, SdkEventType, } from './events.js';
|
|
18
|
+
export class QodoSDK {
|
|
19
|
+
options;
|
|
20
|
+
/** Backend info (get-things) facade */
|
|
21
|
+
info;
|
|
22
|
+
/** Session history facade */
|
|
23
|
+
sessions;
|
|
24
|
+
initialized = false;
|
|
25
|
+
// Environment for AsyncLocalStorage-scoped singletons.
|
|
26
|
+
// Mark as sdkMode to avoid process.exit in SDK code paths.
|
|
27
|
+
env = {
|
|
28
|
+
sdkMode: true,
|
|
29
|
+
sdkDebug: false,
|
|
30
|
+
};
|
|
31
|
+
// SDK is headless by default. We avoid writing to stdout/stderr unless
|
|
32
|
+
// explicitly enabled via options.debug or a provided logger.
|
|
33
|
+
logger;
|
|
34
|
+
// Per-run envelope metadata
|
|
35
|
+
runId = '';
|
|
36
|
+
seq = 0;
|
|
37
|
+
runStartMs = 0;
|
|
38
|
+
sessionContext;
|
|
39
|
+
lastInitMeta = null;
|
|
40
|
+
messageManager;
|
|
41
|
+
agent;
|
|
42
|
+
core;
|
|
43
|
+
eventQueue = [];
|
|
44
|
+
pendingResolvers = [];
|
|
45
|
+
done = false;
|
|
46
|
+
activeRuns = 0;
|
|
47
|
+
// For `sdk.message.delta`
|
|
48
|
+
lastAiText = '';
|
|
49
|
+
lastAiMessageId = '';
|
|
50
|
+
// Deduplication for tool lifecycle
|
|
51
|
+
toolRequestedEmitted = new Set();
|
|
52
|
+
toolApprovalEmitted = new Set();
|
|
53
|
+
toolApprovalInFlight = new Set();
|
|
54
|
+
toolExecutedEmitted = new Set();
|
|
55
|
+
// Track tool names by call id so sdk.tool.executed can include tool_name
|
|
56
|
+
toolRegistry = new Map();
|
|
57
|
+
constructor(options = {}) {
|
|
58
|
+
this.options = options;
|
|
59
|
+
this.info = QodoInfoClient.fromSdkOptions(options);
|
|
60
|
+
this.sessions = QodoSessionsClient.fromSdkOptions(options);
|
|
61
|
+
// If debug is enabled, allow verbose logs in this SDK environment.
|
|
62
|
+
if (this.options.debug === true) {
|
|
63
|
+
this.env.sdkDebug = true;
|
|
64
|
+
}
|
|
65
|
+
const noop = () => { };
|
|
66
|
+
const fallback = this.options.debug
|
|
67
|
+
? {
|
|
68
|
+
debug: console.debug.bind(console),
|
|
69
|
+
info: console.info.bind(console),
|
|
70
|
+
warn: console.warn.bind(console),
|
|
71
|
+
error: console.error.bind(console),
|
|
72
|
+
}
|
|
73
|
+
: { debug: noop, info: noop, warn: noop, error: noop };
|
|
74
|
+
this.logger = {
|
|
75
|
+
debug: this.options.logger?.debug ?? fallback.debug,
|
|
76
|
+
info: this.options.logger?.info ?? fallback.info,
|
|
77
|
+
warn: this.options.logger?.warn ?? fallback.warn,
|
|
78
|
+
error: this.options.logger?.error ?? fallback.error,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
static fromAgent(agentObject, options = {}) {
|
|
82
|
+
return new QodoSDK({ ...options, agentObject });
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create a new SDK instance that will run using a specific backend session id.
|
|
86
|
+
*
|
|
87
|
+
* This is useful for:
|
|
88
|
+
* - resuming or continuing a known session
|
|
89
|
+
* - binding a run to a session id created via `sdk.sessions.createSessionId()`
|
|
90
|
+
*
|
|
91
|
+
* Note: this returns a NEW SDK instance (does not mutate the current one).
|
|
92
|
+
*/
|
|
93
|
+
withSession(sessionId) {
|
|
94
|
+
const nextFlags = { ...(this.options.flags || {}), sid: sessionId };
|
|
95
|
+
return new QodoSDK({ ...this.options, flags: nextFlags });
|
|
96
|
+
}
|
|
97
|
+
pushEvent(type, sessionId, data, opts) {
|
|
98
|
+
const evt = createSdkEvent({
|
|
99
|
+
type,
|
|
100
|
+
run_id: this.runId,
|
|
101
|
+
seq: ++this.seq,
|
|
102
|
+
session_id: sessionId,
|
|
103
|
+
data,
|
|
104
|
+
...(opts?.request_id ? { request_id: opts.request_id } : {}),
|
|
105
|
+
elapsed_ms: this.runStartMs ? Date.now() - this.runStartMs : undefined,
|
|
106
|
+
});
|
|
107
|
+
if (this.pendingResolvers.length > 0) {
|
|
108
|
+
const resolve = this.pendingResolvers.shift();
|
|
109
|
+
resolve({ value: evt, done: false });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.eventQueue.push(evt);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async ensureInitialized(desiredCommandName) {
|
|
116
|
+
if (this.initialized) {
|
|
117
|
+
// Return previously computed summary; best-effort fallback.
|
|
118
|
+
if (this.lastInitMeta)
|
|
119
|
+
return this.lastInitMeta;
|
|
120
|
+
return { commandName: '', promptMode: false, cwd: this.options.cwd ? canonicalizePathSync(this.options.cwd) : canonicalizePathSync(process.cwd()), agentSource: { source: 'default' } };
|
|
121
|
+
}
|
|
122
|
+
// Init backend get-things (supports baseUrl override)
|
|
123
|
+
const bootstrap = new BackendBootstrap({ baseUrlOverride: this.options?.backend?.baseUrl });
|
|
124
|
+
await bootstrap.init();
|
|
125
|
+
const flags = {
|
|
126
|
+
...(this.options.flags || {}),
|
|
127
|
+
sdk: true,
|
|
128
|
+
...(this.options.debug ? { debug: true } : {}),
|
|
129
|
+
};
|
|
130
|
+
if (this.options.model)
|
|
131
|
+
flags.model = this.options.model;
|
|
132
|
+
// Resolve effective cwd first (used for agent discovery and tool defaults)
|
|
133
|
+
const explicitCwd = this.options.cwd ? canonicalizePathSync(this.options.cwd) : undefined;
|
|
134
|
+
const agentFilePathOpt = this.options.agentFile ? canonicalizePathSync(this.options.agentFile) : undefined;
|
|
135
|
+
const derivedCwd = !explicitCwd && agentFilePathOpt ? path.dirname(agentFilePathOpt) : undefined;
|
|
136
|
+
const effectiveCwd = explicitCwd || derivedCwd || canonicalizePathSync(process.cwd());
|
|
137
|
+
// SDK-first agent resolution:
|
|
138
|
+
// 1) agentObject / agentContent / explicit agentFile
|
|
139
|
+
// 2) auto-discover agent.toml|yaml|yml from cwd
|
|
140
|
+
// 3) fallback to built-in minimal config
|
|
141
|
+
let configInput = this.options.agentObject ?? this.options.agentContent ?? this.options.agentFile;
|
|
142
|
+
let isFileContent = !!this.options.agentContent || !!this.options.agentObject;
|
|
143
|
+
let agentSource = { source: 'file' };
|
|
144
|
+
if (this.options.agentObject)
|
|
145
|
+
agentSource = { source: 'object' };
|
|
146
|
+
else if (this.options.agentContent)
|
|
147
|
+
agentSource = { source: 'content' };
|
|
148
|
+
else if (this.options.agentFile)
|
|
149
|
+
agentSource = { source: 'file', path: String(this.options.agentFile) };
|
|
150
|
+
if (!configInput) {
|
|
151
|
+
const discovered = await findNearestAgentConfig(effectiveCwd);
|
|
152
|
+
if (discovered) {
|
|
153
|
+
configInput = discovered;
|
|
154
|
+
isFileContent = false;
|
|
155
|
+
agentSource = { source: 'discovered', path: discovered };
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
configInput = buildDefaultSdkAgentConfig();
|
|
159
|
+
isFileContent = true;
|
|
160
|
+
agentSource = { source: 'default' };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const configManager = await ConfigManager.init(configInput, isFileContent);
|
|
164
|
+
// Resolve command or mode
|
|
165
|
+
let modeCommand;
|
|
166
|
+
if (flags.mode) {
|
|
167
|
+
modeCommand = configManager.resolveModeAsCommand(flags.mode);
|
|
168
|
+
}
|
|
169
|
+
// Determine whether the caller passed a command name or a free-form prompt.
|
|
170
|
+
// SDK should be "prompt-first": if the provided string is not a known command,
|
|
171
|
+
// we still build a command config via getCommandConfig(prompt) (which returns
|
|
172
|
+
// a default config inheriting top-level model/tools) and treat the string as
|
|
173
|
+
// user prompt.
|
|
174
|
+
const allCommands = configManager.listCommands();
|
|
175
|
+
const desiredIsCommand = !!(desiredCommandName && allCommands.includes(desiredCommandName));
|
|
176
|
+
// If the user provided a command name but there are no commands configured,
|
|
177
|
+
// we treat it as a prompt (prompt-first behavior). We also synthesize an
|
|
178
|
+
// internal default command config so execution can proceed.
|
|
179
|
+
const hasAnyCommands = allCommands.length > 0;
|
|
180
|
+
// Choose command:
|
|
181
|
+
// 1) mode (explicit)
|
|
182
|
+
// 2) if desired name is a known command -> use that command
|
|
183
|
+
// 3) else if a non-empty prompt was provided -> treat as prompt and build a default command config
|
|
184
|
+
// 4) else if commands exist -> fallback to flags.command or a single command
|
|
185
|
+
// 5) else (no commands) -> synthesize default command
|
|
186
|
+
let command;
|
|
187
|
+
let cmdName = typeof flags.command === 'string' ? flags.command : configManager.getSingleCommand() || '';
|
|
188
|
+
let promptMode = false;
|
|
189
|
+
if (flags.mode) {
|
|
190
|
+
command = modeCommand;
|
|
191
|
+
cmdName = flags.mode;
|
|
192
|
+
}
|
|
193
|
+
else if (desiredIsCommand) {
|
|
194
|
+
cmdName = desiredCommandName;
|
|
195
|
+
command = configManager.getCommandConfig(cmdName);
|
|
196
|
+
}
|
|
197
|
+
else if (desiredCommandName && String(desiredCommandName).trim().length > 0) {
|
|
198
|
+
cmdName = ''; // prompt flow
|
|
199
|
+
promptMode = true;
|
|
200
|
+
command = configManager.getCommandConfig(desiredCommandName);
|
|
201
|
+
}
|
|
202
|
+
else if (hasAnyCommands) {
|
|
203
|
+
command = configManager.getCommandConfig(cmdName);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// No commands exist: synthesize a default command that inherits top-level
|
|
207
|
+
// model/tools/mcpServers/etc. Prompt() uses this path.
|
|
208
|
+
cmdName = '';
|
|
209
|
+
promptMode = true;
|
|
210
|
+
command = configManager.getCommandConfig('');
|
|
211
|
+
}
|
|
212
|
+
// Tool allow/deny flags
|
|
213
|
+
if (flags.tools) {
|
|
214
|
+
command.available_tools = Array.isArray(flags.tools) ? flags.tools : String(flags.tools).split(',').map((s) => s.trim());
|
|
215
|
+
}
|
|
216
|
+
if (flags.subagents === true) {
|
|
217
|
+
const list = command.available_tools;
|
|
218
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
219
|
+
const hasSubagents = list.some((t) => String(t).split('.')[0] === 'subagents');
|
|
220
|
+
if (!hasSubagents) {
|
|
221
|
+
command.available_tools = [...list, 'subagents'];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (flags.noTools) {
|
|
226
|
+
command.ignore_tools = Array.isArray(flags.noTools) ? flags.noTools : String(flags.noTools).split(',').map((s) => s.trim());
|
|
227
|
+
}
|
|
228
|
+
// MCP servers
|
|
229
|
+
const serverRegistry = ServerRegistry.init(this.options.mcpFile);
|
|
230
|
+
const mcpServers = {
|
|
231
|
+
...configManager.getMCPServers(),
|
|
232
|
+
...(command?.mcpServers || {}),
|
|
233
|
+
...(this.options.mcpServers || {}),
|
|
234
|
+
};
|
|
235
|
+
const disableMcp = command.disable_mcp === true || configManager.getRawConfig()?.disable_mcp === true;
|
|
236
|
+
if (!disableMcp) {
|
|
237
|
+
serverRegistry.setTomlMCPConfigs(mcpServers);
|
|
238
|
+
}
|
|
239
|
+
const generalInstructions = configManager.getGeneralInstructions();
|
|
240
|
+
const systemPrompt = configManager.getSystemPrompt();
|
|
241
|
+
this.sessionContext = await SessionContext.newSessionContext(flags, command, generalInstructions, systemPrompt, undefined, flags.sid);
|
|
242
|
+
// Determine effective working directory
|
|
243
|
+
// Prefer the earlier effectiveCwd we already computed for agent discovery.
|
|
244
|
+
const agentFilePath = this.options.agentFile ? canonicalizePathSync(this.options.agentFile) : undefined;
|
|
245
|
+
const derivedCwd2 = !explicitCwd && agentFilePath ? path.dirname(agentFilePath) : undefined;
|
|
246
|
+
const effectiveCwd2 = effectiveCwd || derivedCwd2 || canonicalizePathSync(process.cwd());
|
|
247
|
+
try {
|
|
248
|
+
this.sessionContext.setExecutionCwd?.(effectiveCwd2);
|
|
249
|
+
}
|
|
250
|
+
catch { }
|
|
251
|
+
const roots = [effectiveCwd2, ...((this.options.projectRoots || []).map((r) => canonicalizePathSync(r)))];
|
|
252
|
+
try {
|
|
253
|
+
await this.sessionContext.setProjectRootPaths(roots);
|
|
254
|
+
}
|
|
255
|
+
catch { }
|
|
256
|
+
if (!disableMcp) {
|
|
257
|
+
await mcpInitialization();
|
|
258
|
+
}
|
|
259
|
+
// Prefetch context summaries from other sessions (equivalent to CLI --with)
|
|
260
|
+
if (this.options.contextSessionIds && this.options.contextSessionIds.length > 0) {
|
|
261
|
+
try {
|
|
262
|
+
const agentTmp = new AgentAPI(this.sessionContext);
|
|
263
|
+
const { computeNextTaskFocus } = await import('../context/taskFocus.js');
|
|
264
|
+
const taskFocus = computeNextTaskFocus(command.instructions, '', extractSetFlags(flags));
|
|
265
|
+
const summaries = await agentTmp.tryPrefetchSummarization(this.options.contextSessionIds, taskFocus);
|
|
266
|
+
for (const [sid, summary] of Object.entries(summaries)) {
|
|
267
|
+
this.sessionContext.addPreviousSessionSummarization(sid, summary);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch { }
|
|
271
|
+
}
|
|
272
|
+
// Per-client message manager and agent
|
|
273
|
+
this.messageManager = new MessageManager();
|
|
274
|
+
this.agent = new AgentAPI(this.sessionContext);
|
|
275
|
+
const backend = ServerData.getInstance();
|
|
276
|
+
this.pushEvent(SdkEventType.Init, this.sessionContext.getSessionId(), {
|
|
277
|
+
sdk_version: getSdkVersion(),
|
|
278
|
+
protocol: 'qodo.sdk.v2',
|
|
279
|
+
pid: process.pid,
|
|
280
|
+
backend: {
|
|
281
|
+
base_url: backend.getBaseUrl(),
|
|
282
|
+
source: backend.getBaseUrlSource(),
|
|
283
|
+
},
|
|
284
|
+
model: this.sessionContext.getModel(),
|
|
285
|
+
});
|
|
286
|
+
// Observe message list changes to emit:
|
|
287
|
+
// - sdk.message.full
|
|
288
|
+
// - sdk.message.delta (best effort from last AI message)
|
|
289
|
+
this.messageManager.addMessageListener((messages) => {
|
|
290
|
+
const lc = toLangChainDict(messages);
|
|
291
|
+
const openai = toOpenAIChat(messages);
|
|
292
|
+
const sid = this.sessionContext.getSessionId();
|
|
293
|
+
this.pushEvent(SdkEventType.MessageFull, sid, {
|
|
294
|
+
messages: { langchain: lc, openai },
|
|
295
|
+
});
|
|
296
|
+
// progress extraction
|
|
297
|
+
try {
|
|
298
|
+
const progress = extractProgress(messages);
|
|
299
|
+
if (progress) {
|
|
300
|
+
this.pushEvent(SdkEventType.Progress, sid, { ...progress });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch { }
|
|
304
|
+
// tool lifecycle extraction from LangChain dict (AIMessage tool_calls + ToolMessage results)
|
|
305
|
+
try {
|
|
306
|
+
this.extractToolLifecycleFromLangChain(lc, sid);
|
|
307
|
+
}
|
|
308
|
+
catch { }
|
|
309
|
+
// delta: compare last AI message string
|
|
310
|
+
try {
|
|
311
|
+
const lastAi = messages
|
|
312
|
+
.slice()
|
|
313
|
+
.reverse()
|
|
314
|
+
.find((m) => m?.constructor?.name === 'AIMessage');
|
|
315
|
+
const content = lastAi?.content;
|
|
316
|
+
const text = typeof content === 'string' ? content : '';
|
|
317
|
+
const messageId = lastAi?.additional_kwargs?.id || lastAi?.id || 'ai';
|
|
318
|
+
if (messageId !== this.lastAiMessageId) {
|
|
319
|
+
this.lastAiMessageId = messageId;
|
|
320
|
+
this.lastAiText = '';
|
|
321
|
+
}
|
|
322
|
+
if (text.length > this.lastAiText.length && text.startsWith(this.lastAiText)) {
|
|
323
|
+
const delta = text.slice(this.lastAiText.length);
|
|
324
|
+
if (delta && delta.length > 0) {
|
|
325
|
+
this.pushEvent(SdkEventType.MessageDelta, sid, {
|
|
326
|
+
message_id: String(messageId),
|
|
327
|
+
role: 'assistant',
|
|
328
|
+
delta,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
this.lastAiText = text;
|
|
333
|
+
}
|
|
334
|
+
catch { }
|
|
335
|
+
});
|
|
336
|
+
// Finalization
|
|
337
|
+
this.messageManager.addLoadingListener(async (isLoading) => {
|
|
338
|
+
const sid = this.sessionContext.getSessionId();
|
|
339
|
+
if (!isLoading) {
|
|
340
|
+
try {
|
|
341
|
+
await this.agent.finishCurrentTask(false);
|
|
342
|
+
}
|
|
343
|
+
catch { }
|
|
344
|
+
// AgentRunnerCore already emits final for old SDK, but we produce our own terminal sdk.final here
|
|
345
|
+
// by reading the message manager state.
|
|
346
|
+
try {
|
|
347
|
+
const finalized = buildFinalResult(this.messageManager);
|
|
348
|
+
this.pushEvent(SdkEventType.Final, sid, {
|
|
349
|
+
success: true,
|
|
350
|
+
model: this.sessionContext.getModel(),
|
|
351
|
+
result: {
|
|
352
|
+
...(finalized.structured_output ? { structured_output: finalized.structured_output } : {}),
|
|
353
|
+
...(finalized.final_output ? { final_output: finalized.final_output } : {}),
|
|
354
|
+
},
|
|
355
|
+
messages: { langchain: finalized.messagesLC, openai: finalized.messagesOpenAI },
|
|
356
|
+
meta: {
|
|
357
|
+
tools_auto_approved: this.options.autoApproveTools !== false,
|
|
358
|
+
subagents_used: finalized.subagentsUsed,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
this.done = true;
|
|
362
|
+
}
|
|
363
|
+
catch (e) {
|
|
364
|
+
this.pushEvent(SdkEventType.Error, sid, { message: e?.message || 'Failed to finalize' });
|
|
365
|
+
this.pushEvent(SdkEventType.Final, sid, {
|
|
366
|
+
success: false,
|
|
367
|
+
error: e?.message || 'Failed to finalize',
|
|
368
|
+
model: this.sessionContext.getModel(),
|
|
369
|
+
result: {},
|
|
370
|
+
messages: { langchain: [], openai: [] },
|
|
371
|
+
meta: { tools_auto_approved: this.options.autoApproveTools !== false, subagents_used: false },
|
|
372
|
+
});
|
|
373
|
+
this.done = true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
this.messageManager.addErrorListener(async (error) => {
|
|
378
|
+
if (!error)
|
|
379
|
+
return;
|
|
380
|
+
const sid = this.sessionContext.getSessionId();
|
|
381
|
+
this.pushEvent(SdkEventType.Error, sid, { message: error });
|
|
382
|
+
try {
|
|
383
|
+
await this.agent.finishCurrentTask(true);
|
|
384
|
+
}
|
|
385
|
+
catch { }
|
|
386
|
+
});
|
|
387
|
+
this.core = new AgentRunnerCore(this.sessionContext, this.messageManager, this.agent,
|
|
388
|
+
// No-op emit: we emit via message manager listeners
|
|
389
|
+
() => { }, this.options.autoApproveTools !== false);
|
|
390
|
+
this.initialized = true;
|
|
391
|
+
this.lastInitMeta = {
|
|
392
|
+
commandName: cmdName,
|
|
393
|
+
promptMode,
|
|
394
|
+
cwd: effectiveCwd2,
|
|
395
|
+
agentSource,
|
|
396
|
+
};
|
|
397
|
+
return this.lastInitMeta;
|
|
398
|
+
}
|
|
399
|
+
async maybeApproveTool(req) {
|
|
400
|
+
const { sid, tool_call_id } = req;
|
|
401
|
+
if (this.toolApprovalEmitted.has(tool_call_id) || this.toolApprovalInFlight.has(tool_call_id)) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
this.toolApprovalInFlight.add(tool_call_id);
|
|
405
|
+
let approved = this.options.autoApproveTools !== false;
|
|
406
|
+
let reason = undefined;
|
|
407
|
+
if (this.options.autoApproveTools === false) {
|
|
408
|
+
if (this.options.toolApproval) {
|
|
409
|
+
try {
|
|
410
|
+
const res = await this.options.toolApproval({
|
|
411
|
+
tool_call_id: req.tool_call_id,
|
|
412
|
+
server_name: req.server_name,
|
|
413
|
+
tool_name: req.tool_name,
|
|
414
|
+
tool_args: req.tool_args,
|
|
415
|
+
reasoning: req.reasoning,
|
|
416
|
+
});
|
|
417
|
+
approved = !!res;
|
|
418
|
+
if (!approved)
|
|
419
|
+
reason = 'Declined by toolApproval callback';
|
|
420
|
+
}
|
|
421
|
+
catch (e) {
|
|
422
|
+
approved = false;
|
|
423
|
+
reason = e?.message || 'toolApproval callback threw';
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
approved = false;
|
|
428
|
+
reason = 'Auto-approval disabled and no toolApproval callback provided';
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// Emit decision
|
|
432
|
+
this.pushEvent(SdkEventType.ToolApproved, sid, {
|
|
433
|
+
tool_call_id,
|
|
434
|
+
approved,
|
|
435
|
+
...(approved ? {} : { reason }),
|
|
436
|
+
});
|
|
437
|
+
this.toolApprovalEmitted.add(tool_call_id);
|
|
438
|
+
// IMPORTANT: respond to backend so the run can continue.
|
|
439
|
+
try {
|
|
440
|
+
await this.agent.toolApproval(tool_call_id, approved);
|
|
441
|
+
}
|
|
442
|
+
catch (e) {
|
|
443
|
+
// Surface approval send failures as sdk.error but do not crash the stream.
|
|
444
|
+
this.pushEvent(SdkEventType.Error, sid, {
|
|
445
|
+
message: `Failed to submit tool approval for ${tool_call_id}`,
|
|
446
|
+
cause: e?.message || e,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
finally {
|
|
450
|
+
this.toolApprovalInFlight.delete(tool_call_id);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
extractToolLifecycleFromLangChain(lcMessages, sid) {
|
|
454
|
+
for (const msg of lcMessages) {
|
|
455
|
+
const type = msg?.type;
|
|
456
|
+
const data = msg?.data;
|
|
457
|
+
if (!type || !data)
|
|
458
|
+
continue;
|
|
459
|
+
// Tool request is represented as AI message with tool_calls
|
|
460
|
+
if (type === 'ai' && Array.isArray(data.tool_calls) && data.tool_calls.length > 0) {
|
|
461
|
+
// Determine pending approval from additional_kwargs
|
|
462
|
+
const pendingApproval = !!data.additional_kwargs?.pending_approval;
|
|
463
|
+
const serverName = data.additional_kwargs?.server_name || 'unknown';
|
|
464
|
+
for (const call of data.tool_calls) {
|
|
465
|
+
const toolCallId = String(call?.id || data.id || 'unknown');
|
|
466
|
+
const toolName = String(call?.name || 'unknown');
|
|
467
|
+
const serverStr = String(serverName);
|
|
468
|
+
// Record mapping for later sdk.tool.executed
|
|
469
|
+
if (toolCallId && toolCallId !== 'unknown') {
|
|
470
|
+
this.toolRegistry.set(toolCallId, { server_name: serverStr, tool_name: toolName });
|
|
471
|
+
}
|
|
472
|
+
// Emit tool.requested once per tool_call_id
|
|
473
|
+
if (!this.toolRequestedEmitted.has(toolCallId)) {
|
|
474
|
+
this.pushEvent(SdkEventType.ToolRequested, sid, {
|
|
475
|
+
tool_call_id: toolCallId,
|
|
476
|
+
server_name: serverStr,
|
|
477
|
+
tool_name: toolName,
|
|
478
|
+
tool_args: call?.args,
|
|
479
|
+
reasoning: typeof data.content === 'string' ? data.content : undefined,
|
|
480
|
+
pending_approval: pendingApproval,
|
|
481
|
+
});
|
|
482
|
+
this.toolRequestedEmitted.add(toolCallId);
|
|
483
|
+
}
|
|
484
|
+
// Approval handling
|
|
485
|
+
if (!this.toolApprovalEmitted.has(toolCallId)) {
|
|
486
|
+
if (pendingApproval) {
|
|
487
|
+
// Approval required: decide and respond via AgentAPI.toolApproval
|
|
488
|
+
void this.maybeApproveTool({
|
|
489
|
+
sid,
|
|
490
|
+
tool_call_id: toolCallId,
|
|
491
|
+
server_name: serverStr,
|
|
492
|
+
tool_name: toolName,
|
|
493
|
+
tool_args: call?.args,
|
|
494
|
+
reasoning: typeof data.content === 'string' ? data.content : undefined,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
// No approval required (auto-approved / read-only)
|
|
499
|
+
this.pushEvent(SdkEventType.ToolApproved, sid, {
|
|
500
|
+
tool_call_id: toolCallId,
|
|
501
|
+
approved: true,
|
|
502
|
+
});
|
|
503
|
+
this.toolApprovalEmitted.add(toolCallId);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// Tool execution result is represented as ToolMessage
|
|
509
|
+
if (type === 'tool') {
|
|
510
|
+
const toolCallId = String(data.tool_call_id || data.id || 'unknown');
|
|
511
|
+
if (!this.toolExecutedEmitted.has(toolCallId)) {
|
|
512
|
+
const serverName = String(data.additional_kwargs?.server_name || this.toolRegistry.get(toolCallId)?.server_name || 'unknown');
|
|
513
|
+
const toolName = String(this.toolRegistry.get(toolCallId)?.tool_name || 'unknown');
|
|
514
|
+
const status = data.status;
|
|
515
|
+
this.pushEvent(SdkEventType.ToolExecuted, sid, {
|
|
516
|
+
tool_call_id: toolCallId,
|
|
517
|
+
server_name: serverName,
|
|
518
|
+
tool_name: toolName,
|
|
519
|
+
result: {
|
|
520
|
+
isError: status === 'error',
|
|
521
|
+
content: data.content,
|
|
522
|
+
raw: data,
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
this.toolExecutedEmitted.add(toolCallId);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async *stream(commandOrPrompt, options) {
|
|
531
|
+
if (this.activeRuns > 0) {
|
|
532
|
+
const delegated = new QodoSDK(this.options);
|
|
533
|
+
try {
|
|
534
|
+
yield* delegated.stream(commandOrPrompt, options);
|
|
535
|
+
}
|
|
536
|
+
finally {
|
|
537
|
+
try {
|
|
538
|
+
await delegated.dispose();
|
|
539
|
+
}
|
|
540
|
+
catch { }
|
|
541
|
+
}
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
this.activeRuns++;
|
|
545
|
+
try {
|
|
546
|
+
this.done = false;
|
|
547
|
+
this.eventQueue = [];
|
|
548
|
+
this.pendingResolvers = [];
|
|
549
|
+
this.lastAiText = '';
|
|
550
|
+
this.lastAiMessageId = '';
|
|
551
|
+
this.toolRequestedEmitted.clear();
|
|
552
|
+
this.toolApprovalEmitted.clear();
|
|
553
|
+
this.toolApprovalInFlight.clear();
|
|
554
|
+
this.toolExecutedEmitted.clear();
|
|
555
|
+
this.toolRegistry.clear();
|
|
556
|
+
// Initialize per-run metadata
|
|
557
|
+
this.runId = uuid();
|
|
558
|
+
this.seq = 0;
|
|
559
|
+
this.runStartMs = Date.now();
|
|
560
|
+
const env = this.env;
|
|
561
|
+
const self = this;
|
|
562
|
+
async function* inner() {
|
|
563
|
+
// If already initialized and user passed a different command name, reinit (same behavior as old QodoClient)
|
|
564
|
+
if (self.initialized) {
|
|
565
|
+
try {
|
|
566
|
+
const cm = ConfigManager.getInstance();
|
|
567
|
+
const cmds = cm.listCommands();
|
|
568
|
+
const isCommand = commandOrPrompt && cmds.includes(commandOrPrompt);
|
|
569
|
+
if (isCommand) {
|
|
570
|
+
const currentName = (self.sessionContext?.getCommand()?.name) || '';
|
|
571
|
+
if (commandOrPrompt !== currentName) {
|
|
572
|
+
await self.dispose();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
catch { }
|
|
577
|
+
}
|
|
578
|
+
let initMeta = null;
|
|
579
|
+
let initFailed = false;
|
|
580
|
+
try {
|
|
581
|
+
initMeta = await self.ensureInitialized(commandOrPrompt);
|
|
582
|
+
}
|
|
583
|
+
catch (e) {
|
|
584
|
+
initFailed = true;
|
|
585
|
+
// Event-first: initialization errors should produce sdk.error + sdk.final
|
|
586
|
+
const sid = '';
|
|
587
|
+
self.pushEvent(SdkEventType.Error, sid, {
|
|
588
|
+
message: e?.message || String(e),
|
|
589
|
+
cause: e?.cause || e,
|
|
590
|
+
});
|
|
591
|
+
self.pushEvent(SdkEventType.Final, sid, {
|
|
592
|
+
success: false,
|
|
593
|
+
error: e?.message || String(e),
|
|
594
|
+
model: self.options.model || '',
|
|
595
|
+
result: {},
|
|
596
|
+
messages: { langchain: [], openai: [] },
|
|
597
|
+
meta: { tools_auto_approved: self.options.autoApproveTools !== false, subagents_used: false },
|
|
598
|
+
});
|
|
599
|
+
// Do NOT return here – we still want to drain the queued events below.
|
|
600
|
+
}
|
|
601
|
+
if (!initFailed) {
|
|
602
|
+
// Emit run.started
|
|
603
|
+
try {
|
|
604
|
+
const sid = self.sessionContext.getSessionId();
|
|
605
|
+
self.pushEvent(SdkEventType.RunStarted, sid, {
|
|
606
|
+
session_id: sid,
|
|
607
|
+
command: initMeta.commandName,
|
|
608
|
+
prompt_mode: initMeta.promptMode,
|
|
609
|
+
cwd: initMeta.cwd,
|
|
610
|
+
agent: initMeta.agentSource,
|
|
611
|
+
tools_auto_approved: self.options.autoApproveTools !== false,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
catch { }
|
|
615
|
+
}
|
|
616
|
+
const extra = options?.extraInstructions || '';
|
|
617
|
+
const args = options?.args || {};
|
|
618
|
+
// Prompt-first behavior:
|
|
619
|
+
// - If commandOrPrompt matches a configured command name -> run that command.
|
|
620
|
+
// - Otherwise treat it as a free-form prompt and pass it as extraInstructions.
|
|
621
|
+
let prompt;
|
|
622
|
+
let promptExtra;
|
|
623
|
+
try {
|
|
624
|
+
const cm = ConfigManager.getInstance();
|
|
625
|
+
const cmds = cm.listCommands();
|
|
626
|
+
if (commandOrPrompt && cmds.includes(commandOrPrompt)) {
|
|
627
|
+
prompt = commandOrPrompt;
|
|
628
|
+
promptExtra = extra;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
prompt = '';
|
|
632
|
+
promptExtra = commandOrPrompt ? `${commandOrPrompt}${extra ? ` ${extra}` : ''}` : extra;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
catch {
|
|
636
|
+
prompt = '';
|
|
637
|
+
promptExtra = commandOrPrompt ? `${commandOrPrompt}${extra ? ` ${extra}` : ''}` : extra;
|
|
638
|
+
}
|
|
639
|
+
if (!initFailed) {
|
|
640
|
+
try {
|
|
641
|
+
// If the command was built with sdkCommand({ args: <zod schema> }), validate args here.
|
|
642
|
+
// This keeps v2 behavior strict and provides actionable errors.
|
|
643
|
+
try {
|
|
644
|
+
const cmdAny = self.sessionContext?.getCommand?.();
|
|
645
|
+
const schema = cmdAny?.__sdk?.argsSchema;
|
|
646
|
+
if (schema) {
|
|
647
|
+
const { parseArgsWithSchema } = await import('./schemas.js');
|
|
648
|
+
parseArgsWithSchema(schema, args);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
catch (e) {
|
|
652
|
+
throw e;
|
|
653
|
+
}
|
|
654
|
+
await self.core.start(prompt, promptExtra, args, options?.outputSchema);
|
|
655
|
+
}
|
|
656
|
+
catch (e) {
|
|
657
|
+
const sid = self.sessionContext?.getSessionId() || '';
|
|
658
|
+
self.pushEvent(SdkEventType.Error, sid, { message: e?.message || String(e) });
|
|
659
|
+
self.pushEvent(SdkEventType.Final, sid, {
|
|
660
|
+
success: false,
|
|
661
|
+
error: e?.message || String(e),
|
|
662
|
+
model: self.sessionContext?.getModel() || '',
|
|
663
|
+
result: {},
|
|
664
|
+
messages: { langchain: [], openai: [] },
|
|
665
|
+
meta: { tools_auto_approved: self.options.autoApproveTools !== false, subagents_used: false },
|
|
666
|
+
});
|
|
667
|
+
// Do not set done=true here; allow the event drain loop to see sdk.final and terminate.
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
while (true) {
|
|
671
|
+
if (self.eventQueue.length > 0) {
|
|
672
|
+
const ev = self.eventQueue.shift();
|
|
673
|
+
// SDK default: keep internal logs muted for the entire stream.
|
|
674
|
+
// (Consumers can still write to stdout directly if they need streaming output.)
|
|
675
|
+
yield ev;
|
|
676
|
+
if (ev.type === SdkEventType.Final) {
|
|
677
|
+
self.done = true;
|
|
678
|
+
// drain
|
|
679
|
+
while (self.eventQueue.length > 0) {
|
|
680
|
+
const drained = self.eventQueue.shift();
|
|
681
|
+
yield drained;
|
|
682
|
+
}
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
const next = await new Promise((resolve) => {
|
|
688
|
+
self.pendingResolvers.push((res) => {
|
|
689
|
+
if (res.done)
|
|
690
|
+
resolve(null);
|
|
691
|
+
else
|
|
692
|
+
resolve(res.value);
|
|
693
|
+
});
|
|
694
|
+
if (self.eventQueue.length > 0) {
|
|
695
|
+
const ev = self.eventQueue.shift();
|
|
696
|
+
const r = self.pendingResolvers.shift();
|
|
697
|
+
if (r)
|
|
698
|
+
r({ value: ev, done: false });
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
if (next) {
|
|
702
|
+
yield next;
|
|
703
|
+
if (next.type === SdkEventType.Final) {
|
|
704
|
+
self.done = true;
|
|
705
|
+
while (self.eventQueue.length > 0) {
|
|
706
|
+
const drained = self.eventQueue.shift();
|
|
707
|
+
yield drained;
|
|
708
|
+
}
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// Only terminate once we've yielded sdk.final and there are no queued events left.
|
|
714
|
+
// (self.done may flip to true from internal listeners before sdk.final is drained.)
|
|
715
|
+
if (self.done && self.eventQueue.length === 0)
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const innerGen = inner();
|
|
720
|
+
while (true) {
|
|
721
|
+
const { value, done } = await runWithEnvironment(env, () => innerGen.next());
|
|
722
|
+
if (done)
|
|
723
|
+
return;
|
|
724
|
+
yield value;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
finally {
|
|
728
|
+
this.activeRuns = Math.max(0, this.activeRuns - 1);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
async run(commandOrPrompt, options) {
|
|
732
|
+
let final = null;
|
|
733
|
+
for await (const ev of this.stream(commandOrPrompt, options)) {
|
|
734
|
+
if (ev.type === SdkEventType.Final)
|
|
735
|
+
final = ev.data;
|
|
736
|
+
}
|
|
737
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
738
|
+
return final;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* SDK-first friendly API: treat input as a free-form prompt (never a command name).
|
|
742
|
+
*/
|
|
743
|
+
prompt(text, options) {
|
|
744
|
+
const extraInstructions = text;
|
|
745
|
+
return this.run('', { ...options, extraInstructions });
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* SDK-first friendly API: stream events for a free-form prompt.
|
|
749
|
+
*/
|
|
750
|
+
streamPrompt(text, options) {
|
|
751
|
+
const extraInstructions = text;
|
|
752
|
+
return this.stream('', { ...options, extraInstructions });
|
|
753
|
+
}
|
|
754
|
+
cancel() {
|
|
755
|
+
if (!this.initialized)
|
|
756
|
+
return;
|
|
757
|
+
this.core.cancel();
|
|
758
|
+
}
|
|
759
|
+
async dispose() {
|
|
760
|
+
if (!this.initialized) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const env = this.env;
|
|
764
|
+
await runWithEnvironment(env, async () => {
|
|
765
|
+
try {
|
|
766
|
+
await this.core.dispose();
|
|
767
|
+
try {
|
|
768
|
+
this.agent.cleanup?.();
|
|
769
|
+
}
|
|
770
|
+
catch { }
|
|
771
|
+
try {
|
|
772
|
+
const { MCPManager } = await import('../mcp/MCPManager.js');
|
|
773
|
+
try {
|
|
774
|
+
await MCPManager.getInstance().dispose();
|
|
775
|
+
}
|
|
776
|
+
catch { }
|
|
777
|
+
}
|
|
778
|
+
catch { }
|
|
779
|
+
}
|
|
780
|
+
finally {
|
|
781
|
+
this.initialized = false;
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
//# sourceMappingURL=QodoSDK.js.map
|