@tiflis-io/tiflis-code-workstation 0.3.9 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +204 -93
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -94,7 +94,7 @@ var EnvSchema = z.object({
|
|
|
94
94
|
// ─────────────────────────────────────────────────────────────
|
|
95
95
|
// Speech-to-Text (STT) Configuration
|
|
96
96
|
// ─────────────────────────────────────────────────────────────
|
|
97
|
-
STT_PROVIDER: z.enum(["openai", "elevenlabs", "deepgram"]).default("openai"),
|
|
97
|
+
STT_PROVIDER: z.enum(["openai", "elevenlabs", "deepgram", "local"]).default("openai"),
|
|
98
98
|
STT_API_KEY: z.string().optional(),
|
|
99
99
|
STT_MODEL: z.string().default("whisper-1"),
|
|
100
100
|
STT_BASE_URL: z.string().url().optional(),
|
|
@@ -102,7 +102,7 @@ var EnvSchema = z.object({
|
|
|
102
102
|
// ─────────────────────────────────────────────────────────────
|
|
103
103
|
// Text-to-Speech (TTS) Configuration
|
|
104
104
|
// ─────────────────────────────────────────────────────────────
|
|
105
|
-
TTS_PROVIDER: z.enum(["openai", "elevenlabs"]).default("openai"),
|
|
105
|
+
TTS_PROVIDER: z.enum(["openai", "elevenlabs", "local"]).default("openai"),
|
|
106
106
|
TTS_API_KEY: z.string().optional(),
|
|
107
107
|
TTS_MODEL: z.string().default("tts-1"),
|
|
108
108
|
TTS_VOICE: z.string().default("alloy"),
|
|
@@ -118,11 +118,11 @@ var EnvSchema = z.object({
|
|
|
118
118
|
// Hide base agent options in mobile apps (only show aliases)
|
|
119
119
|
// ─────────────────────────────────────────────────────────────
|
|
120
120
|
/** Hide base Cursor agent option (only show aliases) */
|
|
121
|
-
HIDE_BASE_CURSOR: z.string().transform((val) => val
|
|
121
|
+
HIDE_BASE_CURSOR: z.string().transform((val) => val.toLowerCase() === "true").default("false"),
|
|
122
122
|
/** Hide base Claude agent option (only show aliases) */
|
|
123
|
-
HIDE_BASE_CLAUDE: z.string().transform((val) => val
|
|
123
|
+
HIDE_BASE_CLAUDE: z.string().transform((val) => val.toLowerCase() === "true").default("false"),
|
|
124
124
|
/** Hide base OpenCode agent option (only show aliases) */
|
|
125
|
-
HIDE_BASE_OPENCODE: z.string().transform((val) => val
|
|
125
|
+
HIDE_BASE_OPENCODE: z.string().transform((val) => val.toLowerCase() === "true").default("false"),
|
|
126
126
|
// ─────────────────────────────────────────────────────────────
|
|
127
127
|
// Terminal Configuration
|
|
128
128
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -134,7 +134,7 @@ var EnvSchema = z.object({
|
|
|
134
134
|
// Mock Mode Configuration (for screenshot automation)
|
|
135
135
|
// ─────────────────────────────────────────────────────────────
|
|
136
136
|
/** Enable mock mode for screenshot automation tests */
|
|
137
|
-
MOCK_MODE: z.string().transform((val) => val
|
|
137
|
+
MOCK_MODE: z.string().transform((val) => val.toLowerCase() === "true").default("false"),
|
|
138
138
|
/** Path to mock fixtures directory (defaults to built-in fixtures) */
|
|
139
139
|
MOCK_FIXTURES_PATH: z.string().optional()
|
|
140
140
|
});
|
|
@@ -155,6 +155,7 @@ function parseAgentAliases() {
|
|
|
155
155
|
let commandStartIndex = 0;
|
|
156
156
|
for (let i = 0; i < parts.length; i++) {
|
|
157
157
|
const part = parts[i];
|
|
158
|
+
if (!part) break;
|
|
158
159
|
const eqIndex = part.indexOf("=");
|
|
159
160
|
if (eqIndex > 0 && !part.startsWith("-")) {
|
|
160
161
|
const varName = part.slice(0, eqIndex);
|
|
@@ -173,6 +174,10 @@ function parseAgentAliases() {
|
|
|
173
174
|
continue;
|
|
174
175
|
}
|
|
175
176
|
const baseCommand = commandParts[0];
|
|
177
|
+
if (!baseCommand) {
|
|
178
|
+
console.warn(`Invalid agent alias ${key}: empty base command`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
176
181
|
const additionalArgs = commandParts.slice(1);
|
|
177
182
|
aliases.set(aliasName, {
|
|
178
183
|
name: aliasName,
|
|
@@ -2315,7 +2320,7 @@ var FileSystemWorkspaceDiscovery = class {
|
|
|
2315
2320
|
}
|
|
2316
2321
|
try {
|
|
2317
2322
|
execSync(`git merge "${sourceBranch}"`, { cwd: projectPath });
|
|
2318
|
-
} catch
|
|
2323
|
+
} catch {
|
|
2319
2324
|
const conflicts = execSync("git diff --name-only --diff-filter=U", {
|
|
2320
2325
|
cwd: projectPath,
|
|
2321
2326
|
encoding: "utf-8"
|
|
@@ -3283,8 +3288,8 @@ function mergeToolBlocks(blocks) {
|
|
|
3283
3288
|
metadata: {
|
|
3284
3289
|
tool_name: block.metadata.tool_name || existing.metadata.tool_name,
|
|
3285
3290
|
tool_use_id: toolUseId,
|
|
3286
|
-
tool_input: block.metadata.tool_input
|
|
3287
|
-
tool_output: block.metadata.tool_output
|
|
3291
|
+
tool_input: block.metadata.tool_input ?? existing.metadata.tool_input,
|
|
3292
|
+
tool_output: block.metadata.tool_output ?? existing.metadata.tool_output,
|
|
3288
3293
|
tool_status: mergedStatus
|
|
3289
3294
|
}
|
|
3290
3295
|
};
|
|
@@ -3341,8 +3346,8 @@ function accumulateBlocks(existing, newBlocks) {
|
|
|
3341
3346
|
metadata: {
|
|
3342
3347
|
tool_name: block.metadata.tool_name || existingBlock.metadata.tool_name,
|
|
3343
3348
|
tool_use_id: toolUseId,
|
|
3344
|
-
tool_input: block.metadata.tool_input
|
|
3345
|
-
tool_output: block.metadata.tool_output
|
|
3349
|
+
tool_input: block.metadata.tool_input ?? existingBlock.metadata.tool_input,
|
|
3350
|
+
tool_output: block.metadata.tool_output ?? existingBlock.metadata.tool_output,
|
|
3346
3351
|
tool_status: mergedStatus
|
|
3347
3352
|
}
|
|
3348
3353
|
};
|
|
@@ -4022,7 +4027,7 @@ var AgentSessionManager = class extends EventEmitter2 {
|
|
|
4022
4027
|
const worktreePath = `/${workspace}/${project}--${branch}`;
|
|
4023
4028
|
const terminatedSessions = [];
|
|
4024
4029
|
for (const [sessionId, state] of this.sessions) {
|
|
4025
|
-
const isInWorktree = state.workingDir.includes(worktreePath) || state.cliSessionId?.includes(`${project}--${branch}`) || state.workingDir.endsWith(`${project}--${branch}`);
|
|
4030
|
+
const isInWorktree = state.workingDir.includes(worktreePath) || (state.cliSessionId?.includes(`${project}--${branch}`) ?? false) || state.workingDir.endsWith(`${project}--${branch}`);
|
|
4026
4031
|
if (isInWorktree) {
|
|
4027
4032
|
try {
|
|
4028
4033
|
if (state.isExecuting) {
|
|
@@ -4044,7 +4049,7 @@ var AgentSessionManager = class extends EventEmitter2 {
|
|
|
4044
4049
|
getWorktreeSessionSummary(workspace, project, branch) {
|
|
4045
4050
|
const worktreePath = `/${workspace}/${project}--${branch}`;
|
|
4046
4051
|
const activeSessions = Array.from(this.sessions.values()).filter(
|
|
4047
|
-
(session) => session.workingDir.includes(worktreePath) || session.cliSessionId?.includes(`${project}--${branch}`) || session.workingDir.endsWith(`${project}--${branch}`)
|
|
4052
|
+
(session) => session.workingDir.includes(worktreePath) || (session.cliSessionId?.includes(`${project}--${branch}`) ?? false) || session.workingDir.endsWith(`${project}--${branch}`)
|
|
4048
4053
|
);
|
|
4049
4054
|
const sessionTypes = [...new Set(activeSessions.map((s) => s.agentType))];
|
|
4050
4055
|
const executingCount = activeSessions.filter((s) => s.isExecuting).length;
|
|
@@ -4691,6 +4696,9 @@ var TerminateSessionUseCase = class {
|
|
|
4691
4696
|
let terminatedInMemory = false;
|
|
4692
4697
|
let terminatedInDb = false;
|
|
4693
4698
|
if (session) {
|
|
4699
|
+
if (isAgentType(session.type)) {
|
|
4700
|
+
this.deps.agentSessionManager.terminateSession(sessionId);
|
|
4701
|
+
}
|
|
4694
4702
|
await this.deps.sessionManager.terminateSession(id);
|
|
4695
4703
|
terminatedInMemory = true;
|
|
4696
4704
|
this.logger.info(
|
|
@@ -4729,6 +4737,42 @@ var TerminateSessionUseCase = class {
|
|
|
4729
4737
|
};
|
|
4730
4738
|
return { response, broadcast };
|
|
4731
4739
|
}
|
|
4740
|
+
/**
|
|
4741
|
+
* Terminates a session and broadcasts the termination to all clients.
|
|
4742
|
+
* Use this for internal calls (e.g., from supervisor tools) where no request/response is needed.
|
|
4743
|
+
* Returns true if session was found and terminated, false otherwise.
|
|
4744
|
+
*/
|
|
4745
|
+
async terminateAndBroadcast(sessionId) {
|
|
4746
|
+
const id = new SessionId(sessionId);
|
|
4747
|
+
this.logger.info({ sessionId }, "Attempting to terminate session (internal)");
|
|
4748
|
+
const session = this.deps.sessionManager.getSession(id);
|
|
4749
|
+
if (session?.type === "supervisor") {
|
|
4750
|
+
throw new Error("Cannot terminate supervisor session");
|
|
4751
|
+
}
|
|
4752
|
+
let terminatedInMemory = false;
|
|
4753
|
+
let terminatedInDb = false;
|
|
4754
|
+
if (session) {
|
|
4755
|
+
if (isAgentType(session.type)) {
|
|
4756
|
+
this.deps.agentSessionManager.terminateSession(sessionId);
|
|
4757
|
+
}
|
|
4758
|
+
await this.deps.sessionManager.terminateSession(id);
|
|
4759
|
+
terminatedInMemory = true;
|
|
4760
|
+
}
|
|
4761
|
+
terminatedInDb = this.deps.chatHistoryService.terminateSession(sessionId);
|
|
4762
|
+
if (!terminatedInMemory && !terminatedInDb) {
|
|
4763
|
+
return false;
|
|
4764
|
+
}
|
|
4765
|
+
const broadcast = {
|
|
4766
|
+
type: "session.terminated",
|
|
4767
|
+
session_id: sessionId
|
|
4768
|
+
};
|
|
4769
|
+
this.deps.messageBroadcaster.broadcastToAll(JSON.stringify(broadcast));
|
|
4770
|
+
this.logger.info(
|
|
4771
|
+
{ sessionId, terminatedInMemory, terminatedInDb },
|
|
4772
|
+
"Session terminated and broadcast sent"
|
|
4773
|
+
);
|
|
4774
|
+
return true;
|
|
4775
|
+
}
|
|
4732
4776
|
};
|
|
4733
4777
|
|
|
4734
4778
|
// src/application/queries/list-sessions.ts
|
|
@@ -6857,16 +6901,7 @@ Steps executed:
|
|
|
6857
6901
|
// src/infrastructure/agents/supervisor/tools/session-tools.ts
|
|
6858
6902
|
import { tool as tool3 } from "@langchain/core/tools";
|
|
6859
6903
|
import { z as z5 } from "zod";
|
|
6860
|
-
function createSessionTools(sessionManager, agentSessionManager, workspaceDiscovery, workspacesRoot,
|
|
6861
|
-
const broadcastTermination = (sessionId) => {
|
|
6862
|
-
const broadcaster = getMessageBroadcaster?.();
|
|
6863
|
-
if (!broadcaster) return;
|
|
6864
|
-
const message = {
|
|
6865
|
-
type: "session.terminated",
|
|
6866
|
-
session_id: sessionId
|
|
6867
|
-
};
|
|
6868
|
-
broadcaster.broadcastToAll(JSON.stringify(message));
|
|
6869
|
-
};
|
|
6904
|
+
function createSessionTools(sessionManager, agentSessionManager, workspaceDiscovery, workspacesRoot, _getMessageBroadcaster, getChatHistoryService, clearSupervisorContext, terminateSessionCallback) {
|
|
6870
6905
|
const listSessions = tool3(
|
|
6871
6906
|
() => {
|
|
6872
6907
|
const chatHistoryService = getChatHistoryService?.();
|
|
@@ -7008,26 +7043,13 @@ Use this tool when user asks to "open terminal", "create terminal", or similar r
|
|
|
7008
7043
|
const terminateSession = tool3(
|
|
7009
7044
|
async ({ sessionId }) => {
|
|
7010
7045
|
try {
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
const session = sessionManager.getSession(id);
|
|
7014
|
-
let terminatedInMemory = false;
|
|
7015
|
-
let terminatedInDb = false;
|
|
7016
|
-
if (session) {
|
|
7017
|
-
if (isAgentType(session.type)) {
|
|
7018
|
-
agentSessionManager.terminateSession(sessionId);
|
|
7019
|
-
}
|
|
7020
|
-
await sessionManager.terminateSession(id);
|
|
7021
|
-
terminatedInMemory = true;
|
|
7022
|
-
}
|
|
7023
|
-
const chatHistoryService = getChatHistoryService?.();
|
|
7024
|
-
if (chatHistoryService) {
|
|
7025
|
-
terminatedInDb = chatHistoryService.terminateSession(sessionId);
|
|
7046
|
+
if (!terminateSessionCallback) {
|
|
7047
|
+
return "Error: Terminate session callback not configured.";
|
|
7026
7048
|
}
|
|
7027
|
-
|
|
7049
|
+
const terminated = await terminateSessionCallback(sessionId);
|
|
7050
|
+
if (!terminated) {
|
|
7028
7051
|
return `Session "${sessionId}" not found.`;
|
|
7029
7052
|
}
|
|
7030
|
-
broadcastTermination(sessionId);
|
|
7031
7053
|
return `Session "${sessionId}" terminated.`;
|
|
7032
7054
|
} catch (error) {
|
|
7033
7055
|
return `Error terminating session: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -7044,6 +7066,9 @@ Use this tool when user asks to "open terminal", "create terminal", or similar r
|
|
|
7044
7066
|
const terminateAllSessions = tool3(
|
|
7045
7067
|
async ({ sessionType }) => {
|
|
7046
7068
|
try {
|
|
7069
|
+
if (!terminateSessionCallback) {
|
|
7070
|
+
return "Error: Terminate session callback not configured.";
|
|
7071
|
+
}
|
|
7047
7072
|
const typeFilter = sessionType === "all" || !sessionType ? null : sessionType;
|
|
7048
7073
|
const chatHistoryService = getChatHistoryService?.();
|
|
7049
7074
|
const inMemorySessions = sessionManager.getAllSessions();
|
|
@@ -7051,31 +7076,25 @@ Use this tool when user asks to "open terminal", "create terminal", or similar r
|
|
|
7051
7076
|
const persistedSessions = chatHistoryService?.getActiveAgentSessions() ?? [];
|
|
7052
7077
|
const inMemoryIds = new Set(inMemorySessions.map((s) => s.id.value));
|
|
7053
7078
|
const persistedToTerminate = persistedSessions.filter((s) => !inMemoryIds.has(s.sessionId)).filter((s) => !typeFilter || s.sessionType === typeFilter);
|
|
7054
|
-
|
|
7079
|
+
const sessionsToTerminate = [
|
|
7080
|
+
...inMemoryToTerminate.map((s) => ({ id: s.id.value, type: s.type })),
|
|
7081
|
+
...persistedToTerminate.map((s) => ({ id: s.sessionId, type: s.sessionType }))
|
|
7082
|
+
];
|
|
7083
|
+
if (sessionsToTerminate.length === 0) {
|
|
7055
7084
|
return typeFilter ? `No active ${typeFilter} sessions to terminate.` : "No active sessions to terminate.";
|
|
7056
7085
|
}
|
|
7057
7086
|
const terminated = [];
|
|
7058
7087
|
const errors = [];
|
|
7059
|
-
for (const session of
|
|
7088
|
+
for (const session of sessionsToTerminate) {
|
|
7060
7089
|
try {
|
|
7061
|
-
|
|
7062
|
-
|
|
7090
|
+
const success = await terminateSessionCallback(session.id);
|
|
7091
|
+
if (success) {
|
|
7092
|
+
terminated.push(`${session.type}:${session.id}`);
|
|
7093
|
+
} else {
|
|
7094
|
+
errors.push(`${session.id}: Session not found`);
|
|
7063
7095
|
}
|
|
7064
|
-
await sessionManager.terminateSession(session.id);
|
|
7065
|
-
chatHistoryService?.terminateSession(session.id.value);
|
|
7066
|
-
broadcastTermination(session.id.value);
|
|
7067
|
-
terminated.push(`${session.type}:${session.id.value}`);
|
|
7068
7096
|
} catch (error) {
|
|
7069
|
-
errors.push(`${session.id
|
|
7070
|
-
}
|
|
7071
|
-
}
|
|
7072
|
-
for (const session of persistedToTerminate) {
|
|
7073
|
-
try {
|
|
7074
|
-
chatHistoryService?.terminateSession(session.sessionId);
|
|
7075
|
-
broadcastTermination(session.sessionId);
|
|
7076
|
-
terminated.push(`${session.sessionType}:${session.sessionId}`);
|
|
7077
|
-
} catch (error) {
|
|
7078
|
-
errors.push(`${session.sessionId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
7097
|
+
errors.push(`${session.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
7079
7098
|
}
|
|
7080
7099
|
}
|
|
7081
7100
|
let result = `Terminated ${terminated.length} session(s)`;
|
|
@@ -7220,6 +7239,23 @@ ${terminatedSessions.map((id) => ` - ${id}`).join("\n")}`;
|
|
|
7220
7239
|
})
|
|
7221
7240
|
}
|
|
7222
7241
|
);
|
|
7242
|
+
const clearContext = tool3(
|
|
7243
|
+
() => {
|
|
7244
|
+
try {
|
|
7245
|
+
if (clearSupervisorContext) {
|
|
7246
|
+
clearSupervisorContext();
|
|
7247
|
+
}
|
|
7248
|
+
return "Supervisor context cleared successfully. Conversation history has been reset.";
|
|
7249
|
+
} catch (error) {
|
|
7250
|
+
return `Error clearing context: ${error instanceof Error ? error.message : String(error)}`;
|
|
7251
|
+
}
|
|
7252
|
+
},
|
|
7253
|
+
{
|
|
7254
|
+
name: "clear_supervisor_context",
|
|
7255
|
+
description: 'Clears the supervisor agent conversation context and history. Use when user asks to "clear context", "reset conversation", "start fresh", or similar requests.',
|
|
7256
|
+
schema: z5.object({})
|
|
7257
|
+
}
|
|
7258
|
+
);
|
|
7223
7259
|
return [
|
|
7224
7260
|
listSessions,
|
|
7225
7261
|
listAvailableAgents,
|
|
@@ -7230,7 +7266,8 @@ ${terminatedSessions.map((id) => ` - ${id}`).join("\n")}`;
|
|
|
7230
7266
|
getSessionInfo,
|
|
7231
7267
|
listSessionsWithWorktrees,
|
|
7232
7268
|
getWorktreeSessionSummary,
|
|
7233
|
-
terminateWorktreeSessions
|
|
7269
|
+
terminateWorktreeSessions,
|
|
7270
|
+
clearContext
|
|
7234
7271
|
];
|
|
7235
7272
|
}
|
|
7236
7273
|
|
|
@@ -7348,6 +7385,8 @@ function formatSize(bytes) {
|
|
|
7348
7385
|
var SupervisorAgent = class extends EventEmitter4 {
|
|
7349
7386
|
logger;
|
|
7350
7387
|
agent;
|
|
7388
|
+
getMessageBroadcaster;
|
|
7389
|
+
getChatHistoryService;
|
|
7351
7390
|
conversationHistory = [];
|
|
7352
7391
|
abortController = null;
|
|
7353
7392
|
isExecuting = false;
|
|
@@ -7359,8 +7398,18 @@ var SupervisorAgent = class extends EventEmitter4 {
|
|
|
7359
7398
|
constructor(config2) {
|
|
7360
7399
|
super();
|
|
7361
7400
|
this.logger = config2.logger.child({ component: "SupervisorAgent" });
|
|
7401
|
+
this.getMessageBroadcaster = config2.getMessageBroadcaster;
|
|
7402
|
+
this.getChatHistoryService = config2.getChatHistoryService;
|
|
7362
7403
|
const env = getEnv();
|
|
7363
7404
|
const llm = this.createLLM(env);
|
|
7405
|
+
const terminateSessionCallback = async (sessionId) => {
|
|
7406
|
+
const terminate = config2.getTerminateSession?.();
|
|
7407
|
+
if (!terminate) {
|
|
7408
|
+
this.logger.warn("Terminate session callback not available");
|
|
7409
|
+
return false;
|
|
7410
|
+
}
|
|
7411
|
+
return terminate(sessionId);
|
|
7412
|
+
};
|
|
7364
7413
|
const tools = [
|
|
7365
7414
|
...createWorkspaceTools(config2.workspaceDiscovery),
|
|
7366
7415
|
...createWorktreeTools(config2.workspaceDiscovery, config2.agentSessionManager),
|
|
@@ -7370,7 +7419,9 @@ var SupervisorAgent = class extends EventEmitter4 {
|
|
|
7370
7419
|
config2.workspaceDiscovery,
|
|
7371
7420
|
config2.workspacesRoot,
|
|
7372
7421
|
config2.getMessageBroadcaster,
|
|
7373
|
-
config2.getChatHistoryService
|
|
7422
|
+
config2.getChatHistoryService,
|
|
7423
|
+
() => this.clearContext(),
|
|
7424
|
+
terminateSessionCallback
|
|
7374
7425
|
),
|
|
7375
7426
|
...createFilesystemTools(config2.workspacesRoot)
|
|
7376
7427
|
];
|
|
@@ -7621,14 +7672,38 @@ var SupervisorAgent = class extends EventEmitter4 {
|
|
|
7621
7672
|
this.logger.debug("Ended command processing");
|
|
7622
7673
|
}
|
|
7623
7674
|
/**
|
|
7624
|
-
* Clears global conversation history.
|
|
7675
|
+
* Clears global conversation history (in-memory only).
|
|
7625
7676
|
* Also resets cancellation state to allow new commands.
|
|
7677
|
+
* @deprecated Use clearContext() for full context clearing with persistence and broadcast.
|
|
7626
7678
|
*/
|
|
7627
7679
|
clearHistory() {
|
|
7628
7680
|
this.conversationHistory = [];
|
|
7629
7681
|
this.isCancelled = false;
|
|
7630
7682
|
this.logger.info("Global conversation history cleared");
|
|
7631
7683
|
}
|
|
7684
|
+
/**
|
|
7685
|
+
* Clears supervisor context completely:
|
|
7686
|
+
* - In-memory conversation history
|
|
7687
|
+
* - Persistent history in database
|
|
7688
|
+
* - Notifies all connected clients
|
|
7689
|
+
*/
|
|
7690
|
+
clearContext() {
|
|
7691
|
+
this.conversationHistory = [];
|
|
7692
|
+
this.isCancelled = false;
|
|
7693
|
+
const chatHistoryService = this.getChatHistoryService?.();
|
|
7694
|
+
if (chatHistoryService) {
|
|
7695
|
+
chatHistoryService.clearSupervisorHistory();
|
|
7696
|
+
}
|
|
7697
|
+
const broadcaster = this.getMessageBroadcaster?.();
|
|
7698
|
+
if (broadcaster) {
|
|
7699
|
+
const clearNotification = JSON.stringify({
|
|
7700
|
+
type: "supervisor.context_cleared",
|
|
7701
|
+
payload: { timestamp: Date.now() }
|
|
7702
|
+
});
|
|
7703
|
+
broadcaster.broadcastToAll(clearNotification);
|
|
7704
|
+
}
|
|
7705
|
+
this.logger.info("Supervisor context cleared (in-memory, persistent, and clients notified)");
|
|
7706
|
+
}
|
|
7632
7707
|
/**
|
|
7633
7708
|
* Resets the cancellation state.
|
|
7634
7709
|
* Call this before starting a new command to ensure previous cancellation doesn't affect it.
|
|
@@ -7864,8 +7939,9 @@ var DEFAULT_FIXTURES_PATH = join7(__dirname, "fixtures");
|
|
|
7864
7939
|
var fixtureCache = /* @__PURE__ */ new Map();
|
|
7865
7940
|
function loadFixture(name, customPath) {
|
|
7866
7941
|
const cacheKey = `${customPath ?? "default"}:${name}`;
|
|
7867
|
-
|
|
7868
|
-
|
|
7942
|
+
const cached = fixtureCache.get(cacheKey);
|
|
7943
|
+
if (cached) {
|
|
7944
|
+
return cached;
|
|
7869
7945
|
}
|
|
7870
7946
|
const fixturesDir = customPath ?? DEFAULT_FIXTURES_PATH;
|
|
7871
7947
|
const filePath = join7(fixturesDir, `${name}.json`);
|
|
@@ -7901,20 +7977,14 @@ async function simulateStreaming(text2, delayMs = DEFAULT_TOKEN_DELAY_MS, onBloc
|
|
|
7901
7977
|
const tokens = tokenize(text2);
|
|
7902
7978
|
let accumulated = "";
|
|
7903
7979
|
for (let i = 0; i < tokens.length; i++) {
|
|
7904
|
-
accumulated += tokens[i];
|
|
7905
|
-
const block =
|
|
7906
|
-
type: "text",
|
|
7907
|
-
text: accumulated
|
|
7908
|
-
};
|
|
7980
|
+
accumulated += tokens[i] ?? "";
|
|
7981
|
+
const block = createTextBlock(accumulated);
|
|
7909
7982
|
onBlock([block], false);
|
|
7910
7983
|
if (i < tokens.length - 1) {
|
|
7911
7984
|
await sleep(delayMs);
|
|
7912
7985
|
}
|
|
7913
7986
|
}
|
|
7914
|
-
const finalBlock =
|
|
7915
|
-
type: "text",
|
|
7916
|
-
text: accumulated
|
|
7917
|
-
};
|
|
7987
|
+
const finalBlock = createTextBlock(accumulated);
|
|
7918
7988
|
onBlock([finalBlock], true);
|
|
7919
7989
|
onComplete();
|
|
7920
7990
|
}
|
|
@@ -7976,7 +8046,7 @@ var MockSupervisorAgent = class extends EventEmitter5 {
|
|
|
7976
8046
|
/**
|
|
7977
8047
|
* Executes a command (non-streaming).
|
|
7978
8048
|
*/
|
|
7979
|
-
|
|
8049
|
+
execute(command, deviceId, _currentSessionId) {
|
|
7980
8050
|
this.logger.info({ command, deviceId }, "Mock supervisor execute");
|
|
7981
8051
|
const response = this.getResponse(command);
|
|
7982
8052
|
this.conversationHistory.push({ role: "user", content: command });
|
|
@@ -8379,7 +8449,18 @@ var STTService = class {
|
|
|
8379
8449
|
this.logger.debug({ audioSize: audioBuffer.length, format }, "Transcribing audio");
|
|
8380
8450
|
const startTime = Date.now();
|
|
8381
8451
|
try {
|
|
8382
|
-
|
|
8452
|
+
let result;
|
|
8453
|
+
switch (this.config.provider) {
|
|
8454
|
+
case "openai":
|
|
8455
|
+
case "local":
|
|
8456
|
+
result = await this.transcribeOpenAI(audioBuffer, format, signal);
|
|
8457
|
+
break;
|
|
8458
|
+
case "elevenlabs":
|
|
8459
|
+
result = await this.transcribeElevenLabs(audioBuffer, format, signal);
|
|
8460
|
+
break;
|
|
8461
|
+
default:
|
|
8462
|
+
throw new Error(`Unsupported STT provider: ${this.config.provider}`);
|
|
8463
|
+
}
|
|
8383
8464
|
const elapsed = Date.now() - startTime;
|
|
8384
8465
|
this.logger.info(
|
|
8385
8466
|
{
|
|
@@ -8491,6 +8572,9 @@ var STTService = class {
|
|
|
8491
8572
|
* Check if the service is configured and ready.
|
|
8492
8573
|
*/
|
|
8493
8574
|
isConfigured() {
|
|
8575
|
+
if (this.config.provider === "local") {
|
|
8576
|
+
return Boolean(this.config.baseUrl && this.config.model);
|
|
8577
|
+
}
|
|
8494
8578
|
return Boolean(this.config.apiKey && this.config.model);
|
|
8495
8579
|
}
|
|
8496
8580
|
/**
|
|
@@ -8507,20 +8591,28 @@ var STTService = class {
|
|
|
8507
8591
|
function createSTTService(env, logger) {
|
|
8508
8592
|
const provider = (env.STT_PROVIDER ?? "openai").toLowerCase();
|
|
8509
8593
|
const apiKey = env.STT_API_KEY;
|
|
8510
|
-
|
|
8594
|
+
const baseUrl = env.STT_BASE_URL;
|
|
8595
|
+
if (provider === "local") {
|
|
8596
|
+
if (!baseUrl) {
|
|
8597
|
+
logger.warn("STT_BASE_URL not configured for local provider, STT service disabled");
|
|
8598
|
+
return null;
|
|
8599
|
+
}
|
|
8600
|
+
} else if (!apiKey) {
|
|
8511
8601
|
logger.warn("STT_API_KEY not configured, STT service disabled");
|
|
8512
8602
|
return null;
|
|
8513
8603
|
}
|
|
8514
8604
|
const defaults = {
|
|
8515
8605
|
openai: { model: "whisper-1" },
|
|
8516
|
-
elevenlabs: { model: "scribe_v1" }
|
|
8606
|
+
elevenlabs: { model: "scribe_v1" },
|
|
8607
|
+
deepgram: { model: "nova-2" },
|
|
8608
|
+
local: { model: "large-v3" }
|
|
8517
8609
|
};
|
|
8518
8610
|
const config2 = {
|
|
8519
8611
|
provider,
|
|
8520
|
-
apiKey,
|
|
8612
|
+
apiKey: apiKey ?? "",
|
|
8521
8613
|
model: env.STT_MODEL ?? defaults[provider].model,
|
|
8522
8614
|
language: env.STT_LANGUAGE,
|
|
8523
|
-
baseUrl
|
|
8615
|
+
baseUrl
|
|
8524
8616
|
};
|
|
8525
8617
|
return new STTService(config2, logger);
|
|
8526
8618
|
}
|
|
@@ -8540,7 +8632,20 @@ var TTSService = class {
|
|
|
8540
8632
|
this.logger.debug({ textLength: text2.length }, "Synthesizing speech");
|
|
8541
8633
|
const startTime = Date.now();
|
|
8542
8634
|
try {
|
|
8543
|
-
|
|
8635
|
+
let result;
|
|
8636
|
+
switch (this.config.provider) {
|
|
8637
|
+
case "openai":
|
|
8638
|
+
case "local":
|
|
8639
|
+
result = await this.synthesizeOpenAI(text2);
|
|
8640
|
+
break;
|
|
8641
|
+
case "elevenlabs":
|
|
8642
|
+
result = await this.synthesizeElevenLabs(text2);
|
|
8643
|
+
break;
|
|
8644
|
+
default: {
|
|
8645
|
+
const exhaustiveCheck = this.config.provider;
|
|
8646
|
+
throw new Error(`Unsupported TTS provider: ${exhaustiveCheck}`);
|
|
8647
|
+
}
|
|
8648
|
+
}
|
|
8544
8649
|
const elapsed = Date.now() - startTime;
|
|
8545
8650
|
this.logger.info(
|
|
8546
8651
|
{ textLength: text2.length, audioSize: result.audio.length, elapsedMs: elapsed },
|
|
@@ -8617,6 +8722,9 @@ var TTSService = class {
|
|
|
8617
8722
|
* Check if the service is configured and ready.
|
|
8618
8723
|
*/
|
|
8619
8724
|
isConfigured() {
|
|
8725
|
+
if (this.config.provider === "local") {
|
|
8726
|
+
return Boolean(this.config.baseUrl && this.config.voice);
|
|
8727
|
+
}
|
|
8620
8728
|
return Boolean(this.config.apiKey && this.config.model && this.config.voice);
|
|
8621
8729
|
}
|
|
8622
8730
|
/**
|
|
@@ -8633,20 +8741,27 @@ var TTSService = class {
|
|
|
8633
8741
|
function createTTSService(env, logger) {
|
|
8634
8742
|
const provider = (env.TTS_PROVIDER ?? "openai").toLowerCase();
|
|
8635
8743
|
const apiKey = env.TTS_API_KEY;
|
|
8636
|
-
|
|
8744
|
+
const baseUrl = env.TTS_BASE_URL;
|
|
8745
|
+
if (provider === "local") {
|
|
8746
|
+
if (!baseUrl) {
|
|
8747
|
+
logger.warn("TTS_BASE_URL not configured for local provider, TTS service disabled");
|
|
8748
|
+
return null;
|
|
8749
|
+
}
|
|
8750
|
+
} else if (!apiKey) {
|
|
8637
8751
|
logger.warn("TTS_API_KEY not configured, TTS service disabled");
|
|
8638
8752
|
return null;
|
|
8639
8753
|
}
|
|
8640
8754
|
const defaults = {
|
|
8641
8755
|
openai: { model: "tts-1", voice: "alloy" },
|
|
8642
|
-
elevenlabs: { model: "eleven_flash_v2_5", voice: "21m00Tcm4TlvDq8ikWAM" }
|
|
8756
|
+
elevenlabs: { model: "eleven_flash_v2_5", voice: "21m00Tcm4TlvDq8ikWAM" },
|
|
8757
|
+
local: { model: "kokoro", voice: "af_heart" }
|
|
8643
8758
|
};
|
|
8644
8759
|
const config2 = {
|
|
8645
8760
|
provider,
|
|
8646
|
-
apiKey,
|
|
8761
|
+
apiKey: apiKey ?? "",
|
|
8647
8762
|
model: env.TTS_MODEL ?? defaults[provider].model,
|
|
8648
8763
|
voice: env.TTS_VOICE ?? defaults[provider].voice,
|
|
8649
|
-
baseUrl
|
|
8764
|
+
baseUrl
|
|
8650
8765
|
};
|
|
8651
8766
|
return new TTSService(config2, logger);
|
|
8652
8767
|
}
|
|
@@ -9066,7 +9181,8 @@ async function bootstrap() {
|
|
|
9066
9181
|
workspacesRoot: env.WORKSPACES_ROOT,
|
|
9067
9182
|
logger,
|
|
9068
9183
|
getMessageBroadcaster: () => messageBroadcaster,
|
|
9069
|
-
getChatHistoryService: () => chatHistoryService
|
|
9184
|
+
getChatHistoryService: () => chatHistoryService,
|
|
9185
|
+
getTerminateSession: () => terminateSession?.terminateAndBroadcast.bind(terminateSession) ?? null
|
|
9070
9186
|
});
|
|
9071
9187
|
if (env.MOCK_MODE) {
|
|
9072
9188
|
logger.info("Mock Supervisor Agent initialized for screenshot automation");
|
|
@@ -9420,7 +9536,7 @@ async function bootstrap() {
|
|
|
9420
9536
|
let commandText;
|
|
9421
9537
|
const messageId = commandMessage.payload.message_id;
|
|
9422
9538
|
if (messageBroadcaster) {
|
|
9423
|
-
const ackMessageId = messageId
|
|
9539
|
+
const ackMessageId = messageId ?? commandMessage.id;
|
|
9424
9540
|
const ackMessage = {
|
|
9425
9541
|
type: "message.ack",
|
|
9426
9542
|
payload: {
|
|
@@ -9678,13 +9794,7 @@ async function bootstrap() {
|
|
|
9678
9794
|
const tunnelClient2 = clearMessage.device_id ? clientRegistry.getByDeviceId(new DeviceId(clearMessage.device_id)) : void 0;
|
|
9679
9795
|
const isAuthenticated = directClient?.isAuthenticated ?? tunnelClient2?.isAuthenticated;
|
|
9680
9796
|
if (isAuthenticated) {
|
|
9681
|
-
supervisorAgent.
|
|
9682
|
-
chatHistoryService.clearSupervisorHistory();
|
|
9683
|
-
const clearNotification = JSON.stringify({
|
|
9684
|
-
type: "supervisor.context_cleared",
|
|
9685
|
-
payload: { timestamp: Date.now() }
|
|
9686
|
-
});
|
|
9687
|
-
broadcaster.broadcastToAll(clearNotification);
|
|
9797
|
+
supervisorAgent.clearContext();
|
|
9688
9798
|
sendToDevice(
|
|
9689
9799
|
socket,
|
|
9690
9800
|
clearMessage.device_id,
|
|
@@ -9907,7 +10017,7 @@ async function bootstrap() {
|
|
|
9907
10017
|
const sessionId = execMessage.session_id;
|
|
9908
10018
|
const messageId = execMessage.payload.message_id;
|
|
9909
10019
|
if (deviceId && messageBroadcaster) {
|
|
9910
|
-
const ackMessageId = messageId
|
|
10020
|
+
const ackMessageId = messageId ?? execMessage.id;
|
|
9911
10021
|
const ackMessage = {
|
|
9912
10022
|
type: "message.ack",
|
|
9913
10023
|
payload: {
|
|
@@ -10657,6 +10767,7 @@ async function bootstrap() {
|
|
|
10657
10767
|
});
|
|
10658
10768
|
terminateSession = new TerminateSessionUseCase({
|
|
10659
10769
|
sessionManager,
|
|
10770
|
+
agentSessionManager,
|
|
10660
10771
|
messageBroadcaster,
|
|
10661
10772
|
chatHistoryService,
|
|
10662
10773
|
logger
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiflis-io/tiflis-code-workstation",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "Workstation server for tiflis-code - manages agent sessions and terminal access",
|
|
5
5
|
"author": "Roman Barinov <rbarinov@gmail.com>",
|
|
6
6
|
"license": "FSL-1.1-NC",
|