tarsk 0.4.30 → 0.5.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +768 -289
- package/dist/public/assets/{account-view-wqPOnMfW.js → account-view-DjLjq8T6.js} +1 -1
- package/dist/public/assets/alert-dialog-BWqb8ENe.js +1 -0
- package/dist/public/assets/api-BbUzw5rb.js +1 -0
- package/dist/public/assets/{browser-tab-BjqSmZHm.js → browser-tab-BVsBMyo3.js} +1 -1
- package/dist/public/assets/chat-input-container-Dh5eJPNP.js +22 -0
- package/dist/public/assets/commit-dialog-CfjW1f6P.js +1 -0
- package/dist/public/assets/{context-menu-XD6khGRr.js → context-menu-D_gX85jr.js} +1 -1
- package/dist/public/assets/conversation-history-view-CKiv8CxD.js +1 -0
- package/dist/public/assets/create-repo-dialog-ByvOv7em.js +1 -0
- package/dist/public/assets/{dialogs-config-BXDglGdB.js → dialogs-config-CV8HzCCA.js} +3 -3
- package/dist/public/assets/diff-view-CuEdnFtI.js +3 -0
- package/dist/public/assets/{explorer-tab-view-DbMBihAH.js → explorer-tab-view-CiDUcvE2.js} +2 -2
- package/dist/public/assets/explorer-tree-cxPx2HiW.js +1 -0
- package/dist/public/assets/{explorer-view-CL_9lhkq.js → explorer-view-BCQNEBOZ.js} +1 -1
- package/dist/public/assets/git-history-dialog-DKey5IQa.js +1 -0
- package/dist/public/assets/history-view-DDpROJvQ.js +1 -0
- package/dist/public/assets/index-BZwvl9X4.js +29 -0
- package/dist/public/assets/index-CihB-pLh.css +1 -0
- package/dist/public/assets/markdown-renderer-B1teF28W.js +10 -0
- package/dist/public/assets/mcp-server-card-CY-VLPhS.js +1 -0
- package/dist/public/assets/merged-pr-dialog-BfwdsytN.js +1 -0
- package/dist/public/assets/onboarding-BEy4bi0g.js +1 -0
- package/dist/public/assets/onboarding-dialog-GjczsXqi.js +1 -0
- package/dist/public/assets/page-toolbar-rWocuukR.js +1 -0
- package/dist/public/assets/{project-settings-view-CRRJPGNL.js → project-settings-view-wlaXVds9.js} +1 -1
- package/dist/public/assets/providers-list-view-BJsp9sta.js +1 -0
- package/dist/public/assets/pull-request-dialog-DDxX2W8n.js +1 -0
- package/dist/public/assets/pull-with-changes-dialog-BtXHWQ3k.js +1 -0
- package/dist/public/assets/push-before-pr-dialog-Cs54Vb76.js +1 -0
- package/dist/public/assets/radio-group-D2kNbZM6.js +1 -0
- package/dist/public/assets/react-vendor-DV_DP0Qd.js +22 -0
- package/dist/public/assets/{resizable-BLhzHL_f.js → resizable-jxPFsLog.js} +1 -1
- package/dist/public/assets/{run-stop-button-CIuDOHFg.js → run-stop-button-N1JNjOlD.js} +2 -2
- package/dist/public/assets/settings-general-view-BKcgxF5z.js +1 -0
- package/dist/public/assets/{settings-instructions-view-B-uTQrNZ.js → settings-instructions-view-DbQ3c3f1.js} +1 -1
- package/dist/public/assets/settings-mcp-servers-view-D2_Brac1.js +5 -0
- package/dist/public/assets/{settings-models-skeleton-CffyGIvJ.js → settings-models-skeleton-BSV1GmZQ.js} +1 -1
- package/dist/public/assets/settings-models-view-BhFuBHAE.js +1 -0
- package/dist/public/assets/settings-rules-view-qL__Zcav.js +8 -0
- package/dist/public/assets/settings-skills-view-Ccj8tTR9.js +2 -0
- package/dist/public/assets/{settings-slash-commands-view-BoT1nCir.js → settings-slash-commands-view-DyXAbQIp.js} +1 -1
- package/dist/public/assets/settings-subagents-view-LcaON0Dq.js +2 -0
- package/dist/public/assets/{settings-view-CS-RnV6J.js → settings-view-cLrxaSw4.js} +2 -2
- package/dist/public/assets/{side-panel-container-B33_hnhc.js → side-panel-container-CCaF3ZtX.js} +2 -2
- package/dist/public/assets/skeleton-DNfHG6mv.js +1 -0
- package/dist/public/assets/standard-list-item-PWOXi212.js +1 -0
- package/dist/public/assets/store-9Wm6Zyve.js +4 -0
- package/dist/public/assets/{tab-context-BUUT3x3d.js → tab-context-BdML89mQ.js} +1 -1
- package/dist/public/assets/tabs-BVcV8raO.js +1 -0
- package/dist/public/assets/{terminal-panel-BCHLDUbD.js → terminal-panel-BG8UFUSX.js} +2 -2
- package/dist/public/assets/textarea-CU-XhvgE.js +1 -0
- package/dist/public/assets/todos-view-DLC_0wx_.js +1 -0
- package/dist/public/assets/use-font-size-Bqn2rvp_.js +1 -0
- package/dist/public/assets/{use-toast-C5s-Xnwi.js → use-toast-CGSBJJb0.js} +1 -1
- package/dist/public/assets/{utils-CDrGT12s.js → utils-Dt4Hs5eF.js} +1 -1
- package/dist/public/assets/{whisper-wasm-C_Ot631g.js → whisper-wasm-BCJQtWoU.js} +2 -2
- package/dist/public/busy.svg +3 -0
- package/dist/public/index.html +22 -22
- package/package.json +1 -1
- package/dist/public/assets/alert-dialog-Dj0hDlZC.js +0 -1
- package/dist/public/assets/api-RbLAI1bg.js +0 -1
- package/dist/public/assets/chat-input-container-BNOl7YtC.js +0 -21
- package/dist/public/assets/conversation-history-view-BRSNoBYn.js +0 -1
- package/dist/public/assets/diff-view-DiNlWcSj.js +0 -3
- package/dist/public/assets/explorer-tree-D0P-HUYS.js +0 -1
- package/dist/public/assets/history-view-i7fGKCkc.js +0 -1
- package/dist/public/assets/index-BeLCtY82.css +0 -1
- package/dist/public/assets/index-CZZ6Jb9i.js +0 -29
- package/dist/public/assets/markdown-renderer-CPSKOcqK.js +0 -10
- package/dist/public/assets/mcp-server-card-DFATg_Ie.js +0 -1
- package/dist/public/assets/onboarding-5CTkrfwe.js +0 -1
- package/dist/public/assets/onboarding-dialog-gcZ9aKKp.js +0 -1
- package/dist/public/assets/page-toolbar-CbwENcML.js +0 -1
- package/dist/public/assets/providers-list-view-D3CIB4ou.js +0 -1
- package/dist/public/assets/radio-group-qCcnwXV3.js +0 -1
- package/dist/public/assets/react-vendor-Bpg1hd5i.js +0 -22
- package/dist/public/assets/settings-general-view-7Y0c9534.js +0 -1
- package/dist/public/assets/settings-mcp-servers-view-nAMQQWHb.js +0 -5
- package/dist/public/assets/settings-models-view-CMZh5LlR.js +0 -1
- package/dist/public/assets/settings-rules-view-BaNccq1v.js +0 -8
- package/dist/public/assets/settings-skills-view-DBX7obT7.js +0 -2
- package/dist/public/assets/settings-subagents-view-Cy87X2bs.js +0 -2
- package/dist/public/assets/skeleton-n47_ST9G.js +0 -1
- package/dist/public/assets/standard-list-item-BLTOBGFa.js +0 -1
- package/dist/public/assets/store-B9rEO6q-.js +0 -4
- package/dist/public/assets/tabs--5y6mYhG.js +0 -1
- package/dist/public/assets/textarea-CwXpBxom.js +0 -1
- package/dist/public/assets/todos-view-k0k8R9NT.js +0 -1
- package/dist/public/assets/use-font-size-BJl84s49.js +0 -1
- /package/dist/public/assets/{dist-CAVGqbBm.js → dist-2eORHB3M.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -265,6 +265,9 @@ async function initializeSchema(db) {
|
|
|
265
265
|
request_model TEXT NOT NULL,
|
|
266
266
|
request_attachments TEXT,
|
|
267
267
|
request_planMode INTEGER,
|
|
268
|
+
request_ralphMode INTEGER NOT NULL DEFAULT 0,
|
|
269
|
+
request_imageMode INTEGER NOT NULL DEFAULT 0,
|
|
270
|
+
request_checkpointRef TEXT,
|
|
268
271
|
response_content TEXT,
|
|
269
272
|
response_events TEXT,
|
|
270
273
|
response_completedAt TEXT,
|
|
@@ -307,6 +310,16 @@ async function initializeSchema(db) {
|
|
|
307
310
|
cachedAt TEXT NOT NULL
|
|
308
311
|
)
|
|
309
312
|
`);
|
|
313
|
+
await db.execute(`
|
|
314
|
+
CREATE TABLE IF NOT EXISTS user_tasks (
|
|
315
|
+
id TEXT PRIMARY KEY,
|
|
316
|
+
threadId TEXT NOT NULL,
|
|
317
|
+
content TEXT NOT NULL,
|
|
318
|
+
createdAt TEXT NOT NULL,
|
|
319
|
+
updatedAt TEXT NOT NULL,
|
|
320
|
+
FOREIGN KEY (threadId) REFERENCES threads(id) ON DELETE CASCADE
|
|
321
|
+
)
|
|
322
|
+
`);
|
|
310
323
|
await db.execute(`
|
|
311
324
|
CREATE TABLE IF NOT EXISTS code_files (
|
|
312
325
|
id INTEGER PRIMARY KEY,
|
|
@@ -356,6 +369,9 @@ async function initializeSchema(db) {
|
|
|
356
369
|
await db.execute(`
|
|
357
370
|
CREATE INDEX IF NOT EXISTS idx_project_todos_projectId ON project_todos(projectId)
|
|
358
371
|
`);
|
|
372
|
+
await db.execute(`
|
|
373
|
+
CREATE INDEX IF NOT EXISTS idx_user_tasks_threadId ON user_tasks(threadId)
|
|
374
|
+
`);
|
|
359
375
|
await db.execute(`
|
|
360
376
|
CREATE INDEX IF NOT EXISTS idx_conversation_history_threadId ON conversation_history(threadId)
|
|
361
377
|
`);
|
|
@@ -674,6 +690,15 @@ async function runMigrations(db) {
|
|
|
674
690
|
);
|
|
675
691
|
await db.execute(`ALTER TABLE conversation_history ADD COLUMN response_imageUrl TEXT`);
|
|
676
692
|
}
|
|
693
|
+
const hasCheckpointRef = convInfo.rows.some(
|
|
694
|
+
(col) => col.name === "request_checkpointRef"
|
|
695
|
+
);
|
|
696
|
+
if (!hasCheckpointRef) {
|
|
697
|
+
tarskDebugLog(
|
|
698
|
+
"[db] Running migration: Adding request_checkpointRef column to conversation_history"
|
|
699
|
+
);
|
|
700
|
+
await db.execute(`ALTER TABLE conversation_history ADD COLUMN request_checkpointRef TEXT`);
|
|
701
|
+
}
|
|
677
702
|
const projectTodosInfo = await db.execute(`PRAGMA table_info(project_todos)`);
|
|
678
703
|
const hasThreadIdColumn = projectTodosInfo.rows.some(
|
|
679
704
|
(col) => col.name === "threadId"
|
|
@@ -695,6 +720,25 @@ async function runMigrations(db) {
|
|
|
695
720
|
)
|
|
696
721
|
`);
|
|
697
722
|
}
|
|
723
|
+
const userTasksExists = await db.execute(
|
|
724
|
+
`SELECT name FROM sqlite_master WHERE type='table' AND name='user_tasks'`
|
|
725
|
+
);
|
|
726
|
+
if (userTasksExists.rows.length === 0) {
|
|
727
|
+
tarskDebugLog("[db] Running migration: Creating user_tasks table");
|
|
728
|
+
await db.execute(`
|
|
729
|
+
CREATE TABLE user_tasks (
|
|
730
|
+
id TEXT PRIMARY KEY,
|
|
731
|
+
threadId TEXT NOT NULL,
|
|
732
|
+
content TEXT NOT NULL,
|
|
733
|
+
createdAt TEXT NOT NULL,
|
|
734
|
+
updatedAt TEXT NOT NULL,
|
|
735
|
+
FOREIGN KEY (threadId) REFERENCES threads(id) ON DELETE CASCADE
|
|
736
|
+
)
|
|
737
|
+
`);
|
|
738
|
+
await db.execute(`
|
|
739
|
+
CREATE INDEX idx_user_tasks_threadId ON user_tasks(threadId)
|
|
740
|
+
`);
|
|
741
|
+
}
|
|
698
742
|
const codeFilesExists = await db.execute(
|
|
699
743
|
`SELECT name FROM sqlite_master WHERE type='table' AND name='code_files'`
|
|
700
744
|
);
|
|
@@ -930,7 +974,7 @@ function estimateTokenCount(text) {
|
|
|
930
974
|
|
|
931
975
|
// src/server.ts
|
|
932
976
|
import fs3 from "fs";
|
|
933
|
-
import { Hono as
|
|
977
|
+
import { Hono as Hono24 } from "hono";
|
|
934
978
|
import { cors } from "hono/cors";
|
|
935
979
|
import open3 from "open";
|
|
936
980
|
import path4 from "path";
|
|
@@ -5077,10 +5121,12 @@ async function createCodingTools(cwd, options) {
|
|
|
5077
5121
|
);
|
|
5078
5122
|
}
|
|
5079
5123
|
let mcpTools = [];
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5124
|
+
if (options?.loadMcpTools !== false) {
|
|
5125
|
+
try {
|
|
5126
|
+
mcpTools = await createMCPTools(cwd);
|
|
5127
|
+
} catch (error) {
|
|
5128
|
+
console.warn(`[Tools] Failed to load MCP tools for ${cwd}:`, error);
|
|
5129
|
+
}
|
|
5084
5130
|
}
|
|
5085
5131
|
const deferredTools = /* @__PURE__ */ new Map();
|
|
5086
5132
|
if (deferOption === false) {
|
|
@@ -6874,6 +6920,114 @@ async function readEnvFile() {
|
|
|
6874
6920
|
// src/features/ask-user/ask-user.routes.ts
|
|
6875
6921
|
import { Hono } from "hono";
|
|
6876
6922
|
|
|
6923
|
+
// src/agent/agent-run-guard.ts
|
|
6924
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
6925
|
+
var LONG_RUNNING_TURN_LIMIT = 50;
|
|
6926
|
+
var LONG_RUNNING_DURATION_MS = 10 * 60 * 1e3;
|
|
6927
|
+
var LONG_RUNNING_CONFIRMATION_QUESTION = "The agent has been working for a long time. Do you want to continue?";
|
|
6928
|
+
var LONG_RUNNING_CONFIRMATION_OPTIONS = ["Yes", "No"];
|
|
6929
|
+
function getLongRunningResolvers() {
|
|
6930
|
+
const pendingQuestions2 = globalThis;
|
|
6931
|
+
if (!pendingQuestions2.__tarskLongRunningResolvers) {
|
|
6932
|
+
pendingQuestions2.__tarskLongRunningResolvers = /* @__PURE__ */ new Map();
|
|
6933
|
+
}
|
|
6934
|
+
return pendingQuestions2.__tarskLongRunningResolvers;
|
|
6935
|
+
}
|
|
6936
|
+
function createLongRunningGuardState(now = Date.now()) {
|
|
6937
|
+
return {
|
|
6938
|
+
startedAt: now,
|
|
6939
|
+
turnCount: 0,
|
|
6940
|
+
prompted: false
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6943
|
+
function incrementLongRunningTurn(state) {
|
|
6944
|
+
state.turnCount += 1;
|
|
6945
|
+
}
|
|
6946
|
+
function resetLongRunningGuard(state, now = Date.now()) {
|
|
6947
|
+
state.startedAt = now;
|
|
6948
|
+
state.turnCount = 0;
|
|
6949
|
+
state.prompted = false;
|
|
6950
|
+
}
|
|
6951
|
+
function shouldPromptForLongRunningConfirmation(state, now = Date.now()) {
|
|
6952
|
+
if (state.prompted) return false;
|
|
6953
|
+
return state.turnCount > LONG_RUNNING_TURN_LIMIT || now - state.startedAt > LONG_RUNNING_DURATION_MS;
|
|
6954
|
+
}
|
|
6955
|
+
function createLongRunningConfirmationRequest() {
|
|
6956
|
+
const toolCallId = `long-running-confirmation-${randomUUID2()}`;
|
|
6957
|
+
const event = {
|
|
6958
|
+
type: "long_running_confirmation",
|
|
6959
|
+
prompt: {
|
|
6960
|
+
toolCallId,
|
|
6961
|
+
question: LONG_RUNNING_CONFIRMATION_QUESTION,
|
|
6962
|
+
options: [...LONG_RUNNING_CONFIRMATION_OPTIONS],
|
|
6963
|
+
context: "The current agent run has exceeded 50 turns or 10 minutes. Choose Yes to continue and reset the limit, or No to stop."
|
|
6964
|
+
},
|
|
6965
|
+
content: JSON.stringify([
|
|
6966
|
+
{
|
|
6967
|
+
type: "tool_use",
|
|
6968
|
+
name: "ask_user",
|
|
6969
|
+
id: toolCallId,
|
|
6970
|
+
input: {
|
|
6971
|
+
question: LONG_RUNNING_CONFIRMATION_QUESTION,
|
|
6972
|
+
options: [...LONG_RUNNING_CONFIRMATION_OPTIONS],
|
|
6973
|
+
context: "The current agent run has exceeded 50 turns or 10 minutes. Choose Yes to continue and reset the limit, or No to stop."
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
])
|
|
6977
|
+
};
|
|
6978
|
+
return {
|
|
6979
|
+
toolCallId,
|
|
6980
|
+
event,
|
|
6981
|
+
waitForAnswer: (signal) => {
|
|
6982
|
+
return new Promise((resolve6, reject) => {
|
|
6983
|
+
const resolvers = getLongRunningResolvers();
|
|
6984
|
+
const cleanup = () => {
|
|
6985
|
+
resolvers.delete(toolCallId);
|
|
6986
|
+
if (signal) {
|
|
6987
|
+
signal.removeEventListener("abort", onAbort);
|
|
6988
|
+
}
|
|
6989
|
+
};
|
|
6990
|
+
const onAbort = () => {
|
|
6991
|
+
cleanup();
|
|
6992
|
+
reject(new Error("Operation aborted"));
|
|
6993
|
+
};
|
|
6994
|
+
if (signal?.aborted) {
|
|
6995
|
+
cleanup();
|
|
6996
|
+
reject(new Error("Operation aborted"));
|
|
6997
|
+
return;
|
|
6998
|
+
}
|
|
6999
|
+
if (signal) {
|
|
7000
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
7001
|
+
}
|
|
7002
|
+
resolvers.set(toolCallId, {
|
|
7003
|
+
resolve: (answer) => {
|
|
7004
|
+
cleanup();
|
|
7005
|
+
resolve6(answer);
|
|
7006
|
+
},
|
|
7007
|
+
reject: (error) => {
|
|
7008
|
+
cleanup();
|
|
7009
|
+
reject(error);
|
|
7010
|
+
}
|
|
7011
|
+
});
|
|
7012
|
+
});
|
|
7013
|
+
}
|
|
7014
|
+
};
|
|
7015
|
+
}
|
|
7016
|
+
function resolveLongRunningConfirmation(toolCallId, answer) {
|
|
7017
|
+
const resolver = getLongRunningResolvers().get(toolCallId);
|
|
7018
|
+
if (!resolver) {
|
|
7019
|
+
return false;
|
|
7020
|
+
}
|
|
7021
|
+
resolver.resolve(answer);
|
|
7022
|
+
return true;
|
|
7023
|
+
}
|
|
7024
|
+
function normalizeLongRunningConfirmationAnswer(answer) {
|
|
7025
|
+
const normalized = answer.trim().toLowerCase();
|
|
7026
|
+
if (normalized === "yes") return "yes";
|
|
7027
|
+
if (normalized === "no") return "no";
|
|
7028
|
+
return null;
|
|
7029
|
+
}
|
|
7030
|
+
|
|
6877
7031
|
// src/core/response-builder.ts
|
|
6878
7032
|
var ResponseBuilder = class {
|
|
6879
7033
|
/**
|
|
@@ -7014,7 +7168,7 @@ function createAskUserRoutes() {
|
|
|
7014
7168
|
400
|
|
7015
7169
|
);
|
|
7016
7170
|
}
|
|
7017
|
-
const resolved = submitAnswer(toolCallId, answer);
|
|
7171
|
+
const resolved = submitAnswer(toolCallId, answer) || resolveLongRunningConfirmation(toolCallId, answer);
|
|
7018
7172
|
if (!resolved) {
|
|
7019
7173
|
return errorResponse(
|
|
7020
7174
|
c,
|
|
@@ -7041,7 +7195,7 @@ function createAskUserRoutes() {
|
|
|
7041
7195
|
import { Hono as Hono2 } from "hono";
|
|
7042
7196
|
|
|
7043
7197
|
// src/features/chat/chat-post.route.ts
|
|
7044
|
-
import { randomUUID as
|
|
7198
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
7045
7199
|
|
|
7046
7200
|
// src/features/skills/skills.manager.ts
|
|
7047
7201
|
import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
|
|
@@ -7615,7 +7769,7 @@ import { join as join15 } from "path";
|
|
|
7615
7769
|
|
|
7616
7770
|
// src/features/project-todos/project-todos.database.ts
|
|
7617
7771
|
init_database();
|
|
7618
|
-
import { randomUUID as
|
|
7772
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
7619
7773
|
function serializeTodo(row) {
|
|
7620
7774
|
return {
|
|
7621
7775
|
...row,
|
|
@@ -7640,7 +7794,7 @@ async function getTodoById(db, todoId) {
|
|
|
7640
7794
|
}
|
|
7641
7795
|
async function insertTodo(projectId, title, description) {
|
|
7642
7796
|
const db = await getDatabase();
|
|
7643
|
-
const id =
|
|
7797
|
+
const id = randomUUID3();
|
|
7644
7798
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7645
7799
|
await db.execute({
|
|
7646
7800
|
sql: `INSERT INTO project_todos (id, projectId, title, description, status, working, createdAt, updatedAt)
|
|
@@ -7750,7 +7904,7 @@ async function invalidateGitStatusCache(db, threadId) {
|
|
|
7750
7904
|
|
|
7751
7905
|
// src/features/account/account-get-info.route.ts
|
|
7752
7906
|
init_database();
|
|
7753
|
-
import { randomUUID as
|
|
7907
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
7754
7908
|
|
|
7755
7909
|
// src/core/crypto.ts
|
|
7756
7910
|
init_database();
|
|
@@ -7903,7 +8057,7 @@ async function getOrCreateClientReferenceId() {
|
|
|
7903
8057
|
if (existing !== null && existing !== void 0) {
|
|
7904
8058
|
return existing;
|
|
7905
8059
|
}
|
|
7906
|
-
const id =
|
|
8060
|
+
const id = randomUUID4();
|
|
7907
8061
|
await setState(db, KEY_CLIENT_REFERENCE_ID, id);
|
|
7908
8062
|
return id;
|
|
7909
8063
|
}
|
|
@@ -8185,7 +8339,8 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
8185
8339
|
provider,
|
|
8186
8340
|
attachments,
|
|
8187
8341
|
planMode,
|
|
8188
|
-
ralphMode
|
|
8342
|
+
ralphMode,
|
|
8343
|
+
checkpointRef
|
|
8189
8344
|
} = body;
|
|
8190
8345
|
let model = baseModel;
|
|
8191
8346
|
if (provider && typeof provider === "string") {
|
|
@@ -8284,7 +8439,7 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
8284
8439
|
}
|
|
8285
8440
|
let conversationId = thread.currentConversationId;
|
|
8286
8441
|
if (!conversationId) {
|
|
8287
|
-
conversationId =
|
|
8442
|
+
conversationId = randomUUID5();
|
|
8288
8443
|
const db = await getDatabase();
|
|
8289
8444
|
await db.execute({
|
|
8290
8445
|
sql: "DELETE FROM todos WHERE threadId = ?",
|
|
@@ -8338,7 +8493,8 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
8338
8493
|
attachments,
|
|
8339
8494
|
planMode,
|
|
8340
8495
|
void 0,
|
|
8341
|
-
ralphMode
|
|
8496
|
+
ralphMode,
|
|
8497
|
+
typeof checkpointRef === "string" ? checkpointRef : void 0
|
|
8342
8498
|
);
|
|
8343
8499
|
const history = await conversationManager.getConversationHistoryByConversationId(
|
|
8344
8500
|
threadId,
|
|
@@ -8366,14 +8522,18 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
8366
8522
|
}
|
|
8367
8523
|
processingStateManager.setProcessing(threadId);
|
|
8368
8524
|
processingStateManager.registerAbortController(threadId, abortController);
|
|
8525
|
+
const longRunningGuard = createLongRunningGuardState();
|
|
8369
8526
|
async function* executeAgent(prompt, ctx, signal, history2, captured) {
|
|
8370
8527
|
for await (const event of agentExecutor.execute(prompt, ctx, signal, history2)) {
|
|
8371
8528
|
captured.events.push(event);
|
|
8529
|
+
if (event.type === "message" || event.type === "toolcall_delta" || event.type === "subagent_event" || event.type === "subagent_start" || event.type === "subagent_complete") {
|
|
8530
|
+
incrementLongRunningTurn(longRunningGuard);
|
|
8531
|
+
}
|
|
8372
8532
|
if (event.type === "message" && event.content) {
|
|
8373
8533
|
captured.fullContent += event.content;
|
|
8374
8534
|
}
|
|
8375
|
-
if (event.type === "message" && typeof event.content === "string" && (event.role === "tool" || isToolLikeContent(event.content))) {
|
|
8376
|
-
const thinkingEvent = { type: "thinking", content: event.content };
|
|
8535
|
+
if (event.type === "message" && typeof event.content === "string" && (event.role === "tool" || isToolLikeContent(event.content)) || event.type === "long_running_confirmation") {
|
|
8536
|
+
const thinkingEvent = { type: "thinking", content: event.content ?? "" };
|
|
8377
8537
|
processingStateManager.pushEvent(threadId, thinkingEvent);
|
|
8378
8538
|
yield thinkingEvent;
|
|
8379
8539
|
continue;
|
|
@@ -8384,6 +8544,44 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
8384
8544
|
}
|
|
8385
8545
|
async function* chatExecutionGenerator() {
|
|
8386
8546
|
const captured = { events: [], fullContent: "" };
|
|
8547
|
+
async function completeCapturedMessage(completedMessageId, completedCaptured) {
|
|
8548
|
+
const finalContent = extractAssistantContent(
|
|
8549
|
+
completedCaptured.events,
|
|
8550
|
+
completedCaptured.fullContent
|
|
8551
|
+
);
|
|
8552
|
+
await conversationManager.completeMessage(
|
|
8553
|
+
completedMessageId,
|
|
8554
|
+
finalContent,
|
|
8555
|
+
completedCaptured.events
|
|
8556
|
+
);
|
|
8557
|
+
return finalContent;
|
|
8558
|
+
}
|
|
8559
|
+
async function* confirmLongRunningWorkIfNeeded(currentMessageId, currentCaptured) {
|
|
8560
|
+
if (!shouldPromptForLongRunningConfirmation(longRunningGuard)) {
|
|
8561
|
+
return true;
|
|
8562
|
+
}
|
|
8563
|
+
longRunningGuard.prompted = true;
|
|
8564
|
+
const confirmation = createLongRunningConfirmationRequest();
|
|
8565
|
+
currentCaptured.events.push(confirmation.event);
|
|
8566
|
+
processingStateManager.pushEvent(threadId, confirmation.event);
|
|
8567
|
+
yield confirmation.event;
|
|
8568
|
+
const answer = await confirmation.waitForAnswer(abortController.signal).catch(() => "No");
|
|
8569
|
+
const normalizedAnswer = normalizeLongRunningConfirmationAnswer(answer);
|
|
8570
|
+
if (normalizedAnswer === "yes") {
|
|
8571
|
+
resetLongRunningGuard(longRunningGuard);
|
|
8572
|
+
return true;
|
|
8573
|
+
}
|
|
8574
|
+
abortController.abort();
|
|
8575
|
+
const stopEvent = {
|
|
8576
|
+
type: "thinking",
|
|
8577
|
+
content: "[Agent] Stopped long-running execution at user request."
|
|
8578
|
+
};
|
|
8579
|
+
currentCaptured.events.push(stopEvent);
|
|
8580
|
+
processingStateManager.pushEvent(threadId, stopEvent);
|
|
8581
|
+
yield stopEvent;
|
|
8582
|
+
await completeCapturedMessage(currentMessageId, currentCaptured);
|
|
8583
|
+
return false;
|
|
8584
|
+
}
|
|
8387
8585
|
try {
|
|
8388
8586
|
yield* executeAgent(
|
|
8389
8587
|
content,
|
|
@@ -8392,8 +8590,16 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
8392
8590
|
conversationHistory,
|
|
8393
8591
|
captured
|
|
8394
8592
|
);
|
|
8395
|
-
const
|
|
8396
|
-
await
|
|
8593
|
+
const initialConfirmation = confirmLongRunningWorkIfNeeded(messageId, captured);
|
|
8594
|
+
const initialDecision = await initialConfirmation.next();
|
|
8595
|
+
if (!initialDecision.done) {
|
|
8596
|
+
yield initialDecision.value;
|
|
8597
|
+
const postPromptDecision = await initialConfirmation.next();
|
|
8598
|
+
if (postPromptDecision.value === false) {
|
|
8599
|
+
return;
|
|
8600
|
+
}
|
|
8601
|
+
}
|
|
8602
|
+
const finalContent = await completeCapturedMessage(messageId, captured);
|
|
8397
8603
|
console.log("[ChatRoute] Conversation captured:", {
|
|
8398
8604
|
messageId,
|
|
8399
8605
|
eventCount: captured.events.length,
|
|
@@ -8421,7 +8627,7 @@ $ ${validationScript}`
|
|
|
8421
8627
|
console.log("[ChatRoute] Validation script passed");
|
|
8422
8628
|
const passEvent = {
|
|
8423
8629
|
type: "thinking",
|
|
8424
|
-
content: `[Validated]
|
|
8630
|
+
content: `[Validated] Validated (exit code 0)${result.output ? `
|
|
8425
8631
|
${result.output}` : ""}`
|
|
8426
8632
|
};
|
|
8427
8633
|
processingStateManager.pushEvent(threadId, passEvent);
|
|
@@ -8463,14 +8669,21 @@ ${result.output}`;
|
|
|
8463
8669
|
updatedHistory,
|
|
8464
8670
|
validationCaptured
|
|
8465
8671
|
);
|
|
8466
|
-
const
|
|
8467
|
-
|
|
8468
|
-
validationCaptured
|
|
8672
|
+
const validationConfirmation = confirmLongRunningWorkIfNeeded(
|
|
8673
|
+
validationMessageId,
|
|
8674
|
+
validationCaptured
|
|
8469
8675
|
);
|
|
8470
|
-
await
|
|
8676
|
+
const validationDecision = await validationConfirmation.next();
|
|
8677
|
+
if (!validationDecision.done) {
|
|
8678
|
+
yield validationDecision.value;
|
|
8679
|
+
const postValidationDecision = await validationConfirmation.next();
|
|
8680
|
+
if (postValidationDecision.value === false) {
|
|
8681
|
+
return;
|
|
8682
|
+
}
|
|
8683
|
+
}
|
|
8684
|
+
const validationContent = await completeCapturedMessage(
|
|
8471
8685
|
validationMessageId,
|
|
8472
|
-
|
|
8473
|
-
validationCaptured.events
|
|
8686
|
+
validationCaptured
|
|
8474
8687
|
);
|
|
8475
8688
|
conversationHistory.push({
|
|
8476
8689
|
userMessage: validationPrompt,
|
|
@@ -8513,7 +8726,7 @@ ${result.output}`;
|
|
|
8513
8726
|
};
|
|
8514
8727
|
processingStateManager.pushEvent(threadId, iterationEvent);
|
|
8515
8728
|
yield iterationEvent;
|
|
8516
|
-
const newConversationId =
|
|
8729
|
+
const newConversationId = randomUUID5();
|
|
8517
8730
|
await threadManager.updateThread(threadId, {
|
|
8518
8731
|
currentConversationId: newConversationId
|
|
8519
8732
|
});
|
|
@@ -8539,15 +8752,16 @@ ${result.output}`;
|
|
|
8539
8752
|
// Empty history - fresh context
|
|
8540
8753
|
iterCaptured
|
|
8541
8754
|
);
|
|
8542
|
-
const
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8755
|
+
const ralphConfirmation = confirmLongRunningWorkIfNeeded(iterMessageId, iterCaptured);
|
|
8756
|
+
const ralphDecision = await ralphConfirmation.next();
|
|
8757
|
+
if (!ralphDecision.done) {
|
|
8758
|
+
yield ralphDecision.value;
|
|
8759
|
+
const postRalphDecision = await ralphConfirmation.next();
|
|
8760
|
+
if (postRalphDecision.value === false) {
|
|
8761
|
+
return;
|
|
8762
|
+
}
|
|
8763
|
+
}
|
|
8764
|
+
await completeCapturedMessage(iterMessageId, iterCaptured);
|
|
8551
8765
|
decrementBalance().catch((err) => {
|
|
8552
8766
|
console.error("[ChatRoute] Failed to decrement balance:", err);
|
|
8553
8767
|
});
|
|
@@ -8637,7 +8851,7 @@ ${result.output}`;
|
|
|
8637
8851
|
}
|
|
8638
8852
|
|
|
8639
8853
|
// src/features/chat/chat-delete.route.ts
|
|
8640
|
-
import { randomUUID as
|
|
8854
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
8641
8855
|
init_database();
|
|
8642
8856
|
async function deleteChat(c, threadManager) {
|
|
8643
8857
|
try {
|
|
@@ -8659,7 +8873,7 @@ async function deleteChat(c, threadManager) {
|
|
|
8659
8873
|
sql: "DELETE FROM todos WHERE threadId = ?",
|
|
8660
8874
|
args: [threadId]
|
|
8661
8875
|
});
|
|
8662
|
-
const newConversationId =
|
|
8876
|
+
const newConversationId = randomUUID6();
|
|
8663
8877
|
await threadManager.updateThread(threadId, { currentConversationId: newConversationId });
|
|
8664
8878
|
return successResponse(
|
|
8665
8879
|
c,
|
|
@@ -8688,13 +8902,10 @@ function subscribeToChatStream(c, processingStateManager) {
|
|
|
8688
8902
|
return c.json({ error: "Thread is not currently processing" }, 404);
|
|
8689
8903
|
}
|
|
8690
8904
|
return stream2(c, async (writer) => {
|
|
8691
|
-
const buffered = processingStateManager.getBufferedEvents(threadId);
|
|
8692
|
-
for (const event of buffered) {
|
|
8693
|
-
await writer.write(JSON.stringify(event) + "\n");
|
|
8694
|
-
}
|
|
8695
8905
|
let resolve6 = null;
|
|
8696
8906
|
const queue = [];
|
|
8697
8907
|
let done = false;
|
|
8908
|
+
const replay = processingStateManager.getBufferedEvents(threadId).slice();
|
|
8698
8909
|
const unsubscribe = processingStateManager.subscribe(threadId, (event) => {
|
|
8699
8910
|
if (event.type === "complete") {
|
|
8700
8911
|
done = true;
|
|
@@ -8707,6 +8918,7 @@ function subscribeToChatStream(c, processingStateManager) {
|
|
|
8707
8918
|
resolve6 = null;
|
|
8708
8919
|
}
|
|
8709
8920
|
});
|
|
8921
|
+
const finishedBeforeSubscribe = !processingStateManager.isProcessing(threadId);
|
|
8710
8922
|
writer.onAbort(() => {
|
|
8711
8923
|
done = true;
|
|
8712
8924
|
unsubscribe();
|
|
@@ -8716,6 +8928,16 @@ function subscribeToChatStream(c, processingStateManager) {
|
|
|
8716
8928
|
}
|
|
8717
8929
|
});
|
|
8718
8930
|
try {
|
|
8931
|
+
for (const event of replay) {
|
|
8932
|
+
await writer.write(JSON.stringify(event) + "\n");
|
|
8933
|
+
if (event.type === "complete") {
|
|
8934
|
+
done = true;
|
|
8935
|
+
}
|
|
8936
|
+
}
|
|
8937
|
+
if (finishedBeforeSubscribe && !done) {
|
|
8938
|
+
await writer.write(JSON.stringify({ type: "complete", content: "" }) + "\n");
|
|
8939
|
+
return;
|
|
8940
|
+
}
|
|
8719
8941
|
while (!done) {
|
|
8720
8942
|
while (queue.length > 0) {
|
|
8721
8943
|
const event = queue.shift();
|
|
@@ -8771,18 +8993,18 @@ function createChatRoutes(threadManager, agentExecutor, conversationManager, pro
|
|
|
8771
8993
|
init_database();
|
|
8772
8994
|
|
|
8773
8995
|
// src/features/conversations/conversations.database.ts
|
|
8774
|
-
import { randomUUID as
|
|
8775
|
-
async function insertMessage(db, threadId, conversationId, message, model, attachments, planMode, imageMode, ralphMode) {
|
|
8996
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
8997
|
+
async function insertMessage(db, threadId, conversationId, message, model, attachments, planMode, imageMode, ralphMode, checkpointRef) {
|
|
8776
8998
|
try {
|
|
8777
|
-
const messageId =
|
|
8999
|
+
const messageId = randomUUID7();
|
|
8778
9000
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
8779
9001
|
await db.execute(
|
|
8780
9002
|
`
|
|
8781
9003
|
INSERT INTO conversation_history (
|
|
8782
9004
|
id, threadId, conversationId, timestamp, status,
|
|
8783
|
-
request_message, request_model, request_attachments, request_planMode, request_ralphMode, request_imageMode
|
|
9005
|
+
request_message, request_model, request_attachments, request_planMode, request_ralphMode, request_imageMode, request_checkpointRef
|
|
8784
9006
|
)
|
|
8785
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9007
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8786
9008
|
`,
|
|
8787
9009
|
[
|
|
8788
9010
|
messageId,
|
|
@@ -8795,7 +9017,8 @@ async function insertMessage(db, threadId, conversationId, message, model, attac
|
|
|
8795
9017
|
attachments ? JSON.stringify(attachments) : null,
|
|
8796
9018
|
planMode ? 1 : 0,
|
|
8797
9019
|
ralphMode ? 1 : 0,
|
|
8798
|
-
imageMode ? 1 : 0
|
|
9020
|
+
imageMode ? 1 : 0,
|
|
9021
|
+
checkpointRef ?? null
|
|
8799
9022
|
]
|
|
8800
9023
|
);
|
|
8801
9024
|
return messageId;
|
|
@@ -8999,7 +9222,8 @@ function deserializeMessage(row) {
|
|
|
8999
9222
|
...row.request_attachments && { attachments: JSON.parse(row.request_attachments) },
|
|
9000
9223
|
...row.request_planMode === 1 && { planMode: true },
|
|
9001
9224
|
...row.request_ralphMode === 1 && { ralphMode: true },
|
|
9002
|
-
...row.request_imageMode === 1 && { imageMode: true }
|
|
9225
|
+
...row.request_imageMode === 1 && { imageMode: true },
|
|
9226
|
+
...row.request_checkpointRef && { checkpointRef: row.request_checkpointRef }
|
|
9003
9227
|
},
|
|
9004
9228
|
response: row.response_content != null ? {
|
|
9005
9229
|
content: row.response_content,
|
|
@@ -9078,7 +9302,7 @@ var ConversationManagerImpl = class {
|
|
|
9078
9302
|
/**
|
|
9079
9303
|
* Starts a new conversation message
|
|
9080
9304
|
*/
|
|
9081
|
-
async startMessage(threadId, _threadPath, conversationId, message, model, attachments, planMode, imageMode, ralphMode) {
|
|
9305
|
+
async startMessage(threadId, _threadPath, conversationId, message, model, attachments, planMode, imageMode, ralphMode, checkpointRef) {
|
|
9082
9306
|
try {
|
|
9083
9307
|
if (!this.db) {
|
|
9084
9308
|
await this.initialize();
|
|
@@ -9092,7 +9316,8 @@ var ConversationManagerImpl = class {
|
|
|
9092
9316
|
attachments,
|
|
9093
9317
|
planMode,
|
|
9094
9318
|
imageMode,
|
|
9095
|
-
ralphMode
|
|
9319
|
+
ralphMode,
|
|
9320
|
+
checkpointRef
|
|
9096
9321
|
);
|
|
9097
9322
|
} catch (error) {
|
|
9098
9323
|
console.error("Failed to start conversation message:", error);
|
|
@@ -9978,7 +10203,8 @@ async function computeContextBreakdown(threadId, conversationId, conversationMan
|
|
|
9978
10203
|
skills: activatedSkills,
|
|
9979
10204
|
threadId,
|
|
9980
10205
|
threadPath,
|
|
9981
|
-
metadataManager
|
|
10206
|
+
metadataManager,
|
|
10207
|
+
loadMcpTools: false
|
|
9982
10208
|
});
|
|
9983
10209
|
const promptSections = await loadPromptSections(
|
|
9984
10210
|
threadPath,
|
|
@@ -10210,6 +10436,14 @@ async function deleteThread(db, id) {
|
|
|
10210
10436
|
throw error;
|
|
10211
10437
|
}
|
|
10212
10438
|
}
|
|
10439
|
+
async function getThread(db, id) {
|
|
10440
|
+
const result = await db.execute("SELECT * FROM threads WHERE id = ?", [id]);
|
|
10441
|
+
const row = result.rows[0];
|
|
10442
|
+
if (!row) {
|
|
10443
|
+
return null;
|
|
10444
|
+
}
|
|
10445
|
+
return deserializeThread(row);
|
|
10446
|
+
}
|
|
10213
10447
|
async function getAllThreads(db) {
|
|
10214
10448
|
try {
|
|
10215
10449
|
const result = await db.execute(
|
|
@@ -12477,7 +12711,7 @@ var ProcessManager = class extends EventEmitter {
|
|
|
12477
12711
|
|
|
12478
12712
|
// src/features/projects/projects.creator.ts
|
|
12479
12713
|
init_utils();
|
|
12480
|
-
import { randomUUID as
|
|
12714
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
12481
12715
|
import { basename as basename2, join as join20, relative as relative5 } from "path";
|
|
12482
12716
|
import { access as access3, stat as stat3, readdir as readdir8 } from "fs/promises";
|
|
12483
12717
|
|
|
@@ -13663,14 +13897,14 @@ var ProjectCreator = class {
|
|
|
13663
13897
|
}
|
|
13664
13898
|
let initialThreadId;
|
|
13665
13899
|
try {
|
|
13666
|
-
const projectId =
|
|
13900
|
+
const projectId = randomUUID8();
|
|
13667
13901
|
const existingProjects = await this.metadataManager.loadProjects();
|
|
13668
13902
|
const existingNames = existingProjects.map((p) => p.name);
|
|
13669
13903
|
const projectName = this.ensureUniqueProjectName(
|
|
13670
13904
|
this.deriveProjectName(gitUrl),
|
|
13671
13905
|
existingNames
|
|
13672
13906
|
);
|
|
13673
|
-
initialThreadId =
|
|
13907
|
+
initialThreadId = randomUUID8();
|
|
13674
13908
|
const initialThreadTitle = generateRandomThreadName();
|
|
13675
13909
|
const firstThreadPath = this.generateThreadPath(projectId, initialThreadId);
|
|
13676
13910
|
this.processingStateManager?.setProcessing(initialThreadId);
|
|
@@ -13810,8 +14044,8 @@ var ProjectCreator = class {
|
|
|
13810
14044
|
};
|
|
13811
14045
|
return;
|
|
13812
14046
|
}
|
|
13813
|
-
const projectId =
|
|
13814
|
-
const initialThreadId =
|
|
14047
|
+
const projectId = randomUUID8();
|
|
14048
|
+
const initialThreadId = randomUUID8();
|
|
13815
14049
|
const initialThreadTitle = generateRandomThreadName();
|
|
13816
14050
|
this.processingStateManager?.setProcessing(initialThreadId);
|
|
13817
14051
|
try {
|
|
@@ -13961,8 +14195,8 @@ var ProjectCreator = class {
|
|
|
13961
14195
|
yield { type: "error", message: `Cannot access folder: ${folderPath}` };
|
|
13962
14196
|
return;
|
|
13963
14197
|
}
|
|
13964
|
-
const projectId =
|
|
13965
|
-
const initialThreadId =
|
|
14198
|
+
const projectId = randomUUID8();
|
|
14199
|
+
const initialThreadId = randomUUID8();
|
|
13966
14200
|
const initialThreadTitle = generateRandomThreadName();
|
|
13967
14201
|
this.processingStateManager?.setProcessing(initialThreadId);
|
|
13968
14202
|
try {
|
|
@@ -14396,7 +14630,7 @@ var ProjectManagerImpl = class {
|
|
|
14396
14630
|
throw new Error(`Project not found: ${projectId}`);
|
|
14397
14631
|
}
|
|
14398
14632
|
if (setupScript === null || setupScript === "") {
|
|
14399
|
-
|
|
14633
|
+
Reflect.set(projects[projectIndex], "setupScript", null);
|
|
14400
14634
|
} else {
|
|
14401
14635
|
projects[projectIndex].setupScript = setupScript;
|
|
14402
14636
|
}
|
|
@@ -14414,7 +14648,7 @@ var ProjectManagerImpl = class {
|
|
|
14414
14648
|
throw new Error(`Project not found: ${projectId}`);
|
|
14415
14649
|
}
|
|
14416
14650
|
if (validationScript === null || validationScript === "") {
|
|
14417
|
-
|
|
14651
|
+
Reflect.set(projects[projectIndex], "validationScript", null);
|
|
14418
14652
|
} else {
|
|
14419
14653
|
projects[projectIndex].validationScript = validationScript;
|
|
14420
14654
|
}
|
|
@@ -14536,11 +14770,15 @@ ___CWD___%s
|
|
|
14536
14770
|
*/
|
|
14537
14771
|
async updateRunCommand(projectId, runCommand) {
|
|
14538
14772
|
const projects = await this.metadataManager.loadProjects();
|
|
14539
|
-
const
|
|
14540
|
-
if (
|
|
14773
|
+
const projectIndex = projects.findIndex((p) => p.id === projectId);
|
|
14774
|
+
if (projectIndex === -1) {
|
|
14541
14775
|
throw new Error(`Project not found: ${projectId}`);
|
|
14542
14776
|
}
|
|
14543
|
-
|
|
14777
|
+
if (runCommand === null || runCommand === "") {
|
|
14778
|
+
Reflect.set(projects[projectIndex], "runCommand", null);
|
|
14779
|
+
} else {
|
|
14780
|
+
projects[projectIndex].runCommand = runCommand;
|
|
14781
|
+
}
|
|
14544
14782
|
await this.metadataManager.saveProjects(projects);
|
|
14545
14783
|
}
|
|
14546
14784
|
/**
|
|
@@ -14569,7 +14807,7 @@ ___CWD___%s
|
|
|
14569
14807
|
if (!project) {
|
|
14570
14808
|
throw new Error(`Project not found: ${projectId}`);
|
|
14571
14809
|
}
|
|
14572
|
-
project
|
|
14810
|
+
Reflect.set(project, field, value && value.trim() ? value : null);
|
|
14573
14811
|
await this.metadataManager.saveProjects(projects);
|
|
14574
14812
|
}
|
|
14575
14813
|
/**
|
|
@@ -15946,7 +16184,7 @@ function createRuleRoutes(router, projectManager) {
|
|
|
15946
16184
|
|
|
15947
16185
|
// src/features/threads/threads.manager.ts
|
|
15948
16186
|
init_utils();
|
|
15949
|
-
import { randomUUID as
|
|
16187
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
15950
16188
|
import { join as join23 } from "path";
|
|
15951
16189
|
import { execSync as execSync3 } from "child_process";
|
|
15952
16190
|
import { rm as rm4, stat as stat4, mkdir as mkdir4 } from "fs/promises";
|
|
@@ -16013,7 +16251,7 @@ var ThreadManagerImpl = class {
|
|
|
16013
16251
|
} catch {
|
|
16014
16252
|
await mkdir4(project.path, { recursive: true });
|
|
16015
16253
|
}
|
|
16016
|
-
threadId =
|
|
16254
|
+
threadId = randomUUID9();
|
|
16017
16255
|
const threadPath = this.generateThreadPath(projectId, threadId);
|
|
16018
16256
|
const existingThreads = await this.metadataManager.loadThreads();
|
|
16019
16257
|
const existingTitles = new Set(
|
|
@@ -16554,7 +16792,7 @@ import { glob as glob2 } from "glob";
|
|
|
16554
16792
|
|
|
16555
16793
|
// src/features/project-scripts/project-scripts.database.ts
|
|
16556
16794
|
init_database();
|
|
16557
|
-
import { randomUUID as
|
|
16795
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
16558
16796
|
async function getScriptsByProject(db, projectId) {
|
|
16559
16797
|
const result = await db.execute({
|
|
16560
16798
|
sql: `SELECT id, projectId, workspace, name, command, friendlyName, updatedAt
|
|
@@ -16571,7 +16809,7 @@ async function upsertProjectScripts(projectId, scripts) {
|
|
|
16571
16809
|
sql: `INSERT OR REPLACE INTO project_scripts (id, projectId, workspace, name, command, friendlyName, updatedAt)
|
|
16572
16810
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
16573
16811
|
args: [
|
|
16574
|
-
|
|
16812
|
+
randomUUID10(),
|
|
16575
16813
|
projectId,
|
|
16576
16814
|
script.workspace,
|
|
16577
16815
|
script.name,
|
|
@@ -17078,7 +17316,8 @@ async function handleGetThreadMessages(c, threadManager, conversationManager) {
|
|
|
17078
17316
|
fileName: attachment.name,
|
|
17079
17317
|
content: attachment.content,
|
|
17080
17318
|
size: attachment.size
|
|
17081
|
-
}))
|
|
17319
|
+
})),
|
|
17320
|
+
checkpointRef: message.request.checkpointRef
|
|
17082
17321
|
};
|
|
17083
17322
|
if (!message.response) {
|
|
17084
17323
|
return [userMessage];
|
|
@@ -18652,6 +18891,101 @@ import { existsSync as existsSync18 } from "fs";
|
|
|
18652
18891
|
|
|
18653
18892
|
// src/features/git/git.utils.ts
|
|
18654
18893
|
init_utils();
|
|
18894
|
+
|
|
18895
|
+
// src/features/git/git-subprocess-timing.ts
|
|
18896
|
+
init_utils();
|
|
18897
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
18898
|
+
var GITOPS_TIMING_PREFIX = "[gitops-timing]";
|
|
18899
|
+
var timingStorage = new AsyncLocalStorage();
|
|
18900
|
+
function withGitTimingContext(threadId, fn) {
|
|
18901
|
+
return timingStorage.run({ threadId }, fn);
|
|
18902
|
+
}
|
|
18903
|
+
function isGitSubprocessTimingEnabled() {
|
|
18904
|
+
return timingStorage.getStore() !== void 0;
|
|
18905
|
+
}
|
|
18906
|
+
function logSubprocessMark(scope, stepName, label, offsetMs) {
|
|
18907
|
+
console.log(
|
|
18908
|
+
`${GITOPS_TIMING_PREFIX} subprocess ${scope} step=${stepName} ${label} +${offsetMs}ms`
|
|
18909
|
+
);
|
|
18910
|
+
}
|
|
18911
|
+
function runCommandTimed(stepName, command, args2, cwd) {
|
|
18912
|
+
const scope = `thread=${timingStorage.getStore()?.threadId ?? "unknown"}`;
|
|
18913
|
+
const t0 = Date.now();
|
|
18914
|
+
let tSpawnEvent = null;
|
|
18915
|
+
let tFirstIo = null;
|
|
18916
|
+
let tExit = null;
|
|
18917
|
+
logSubprocessMark(scope, stepName, "spawn-call", 0);
|
|
18918
|
+
return new Promise((resolve6, reject) => {
|
|
18919
|
+
const proc = spawnProcess(command, args2, { cwd });
|
|
18920
|
+
proc.on("spawn", () => {
|
|
18921
|
+
tSpawnEvent = Date.now();
|
|
18922
|
+
logSubprocessMark(scope, stepName, "spawn-event", tSpawnEvent - t0);
|
|
18923
|
+
});
|
|
18924
|
+
let stdout = "";
|
|
18925
|
+
let stderr = "";
|
|
18926
|
+
function onIo() {
|
|
18927
|
+
if (tFirstIo === null) {
|
|
18928
|
+
tFirstIo = Date.now();
|
|
18929
|
+
logSubprocessMark(scope, stepName, "first-io", tFirstIo - t0);
|
|
18930
|
+
}
|
|
18931
|
+
}
|
|
18932
|
+
if (proc.stdout) {
|
|
18933
|
+
proc.stdout.on("data", (chunk) => {
|
|
18934
|
+
onIo();
|
|
18935
|
+
stdout += chunk.toString();
|
|
18936
|
+
});
|
|
18937
|
+
}
|
|
18938
|
+
if (proc.stderr) {
|
|
18939
|
+
proc.stderr.on("data", (chunk) => {
|
|
18940
|
+
onIo();
|
|
18941
|
+
stderr += chunk.toString();
|
|
18942
|
+
});
|
|
18943
|
+
}
|
|
18944
|
+
proc.on("exit", () => {
|
|
18945
|
+
tExit = Date.now();
|
|
18946
|
+
logSubprocessMark(scope, stepName, "process-exit", tExit - t0);
|
|
18947
|
+
});
|
|
18948
|
+
proc.on("close", (code) => {
|
|
18949
|
+
const tClose = Date.now();
|
|
18950
|
+
logSubprocessMark(scope, stepName, "close-callback", tClose - t0);
|
|
18951
|
+
const preSpawnMs = tSpawnEvent !== null ? tSpawnEvent - t0 : null;
|
|
18952
|
+
const processActiveMs = tSpawnEvent !== null && tExit !== null ? tExit - tSpawnEvent : null;
|
|
18953
|
+
const exitToCloseMs = tExit !== null ? tClose - tExit : null;
|
|
18954
|
+
console.log(
|
|
18955
|
+
`${GITOPS_TIMING_PREFIX} subprocess ${scope} step=${stepName} summary total=${tClose - t0}ms` + (preSpawnMs !== null ? ` pre-spawn=${preSpawnMs}ms` : "") + (processActiveMs !== null ? ` process-active=${processActiveMs}ms` : "") + (exitToCloseMs !== null ? ` exit-to-close=${exitToCloseMs}ms` : "") + ` cmd=${command} ${args2.join(" ")}`
|
|
18956
|
+
);
|
|
18957
|
+
resolve6({ code, stdout, stderr });
|
|
18958
|
+
});
|
|
18959
|
+
proc.on("error", (error) => {
|
|
18960
|
+
logSubprocessMark(scope, stepName, "spawn-error", Date.now() - t0);
|
|
18961
|
+
reject(error);
|
|
18962
|
+
});
|
|
18963
|
+
});
|
|
18964
|
+
}
|
|
18965
|
+
function runGitCommandTimed(stepName, cwd, args2) {
|
|
18966
|
+
return runCommandTimed(stepName, "git", args2, cwd);
|
|
18967
|
+
}
|
|
18968
|
+
var EVENT_LOOP_LAG_THRESHOLD_MS = 50;
|
|
18969
|
+
var EVENT_LOOP_SAMPLE_INTERVAL_MS = 100;
|
|
18970
|
+
function startEventLoopLagMonitor(threadId) {
|
|
18971
|
+
const requestStart = Date.now();
|
|
18972
|
+
let expectedAt = requestStart + EVENT_LOOP_SAMPLE_INTERVAL_MS;
|
|
18973
|
+
const intervalId = setInterval(() => {
|
|
18974
|
+
const now = Date.now();
|
|
18975
|
+
const lag = now - expectedAt;
|
|
18976
|
+
if (lag >= EVENT_LOOP_LAG_THRESHOLD_MS) {
|
|
18977
|
+
console.log(
|
|
18978
|
+
`${GITOPS_TIMING_PREFIX} event-loop-lag thread=${threadId} lag=${lag}ms at=+${now - requestStart}ms`
|
|
18979
|
+
);
|
|
18980
|
+
}
|
|
18981
|
+
expectedAt = now + EVENT_LOOP_SAMPLE_INTERVAL_MS;
|
|
18982
|
+
}, EVENT_LOOP_SAMPLE_INTERVAL_MS);
|
|
18983
|
+
return () => {
|
|
18984
|
+
clearInterval(intervalId);
|
|
18985
|
+
};
|
|
18986
|
+
}
|
|
18987
|
+
|
|
18988
|
+
// src/features/git/git.utils.ts
|
|
18655
18989
|
import { existsSync as existsSync17, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
18656
18990
|
import { isAbsolute as isAbsolute3, normalize as normalize2, resolve as resolve3, join as join28 } from "path";
|
|
18657
18991
|
import { completeSimple } from "@mariozechner/pi-ai";
|
|
@@ -18796,32 +19130,61 @@ function resolveThreadPath(repoPath) {
|
|
|
18796
19130
|
const base = isAbsolute3(repoPath) ? repoPath : resolve3(getDataDir(), repoPath);
|
|
18797
19131
|
return normalize2(base);
|
|
18798
19132
|
}
|
|
18799
|
-
|
|
18800
|
-
|
|
18801
|
-
|
|
18802
|
-
|
|
18803
|
-
|
|
18804
|
-
|
|
18805
|
-
|
|
18806
|
-
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
|
|
18810
|
-
|
|
18811
|
-
|
|
18812
|
-
|
|
19133
|
+
var gitRootCache = /* @__PURE__ */ new Map();
|
|
19134
|
+
async function getGitRoot(cwd) {
|
|
19135
|
+
const cacheKey = normalize2(cwd);
|
|
19136
|
+
const cached = gitRootCache.get(cacheKey);
|
|
19137
|
+
if (cached) {
|
|
19138
|
+
return cached;
|
|
19139
|
+
}
|
|
19140
|
+
let gitRoot;
|
|
19141
|
+
if (isGitSubprocessTimingEnabled()) {
|
|
19142
|
+
const result = await runGitCommandTimed("resolve-git-root", cwd, [
|
|
19143
|
+
"rev-parse",
|
|
19144
|
+
"--show-toplevel"
|
|
19145
|
+
]);
|
|
19146
|
+
if (result.code === 0) {
|
|
19147
|
+
gitRoot = result.stdout.trim();
|
|
19148
|
+
} else {
|
|
19149
|
+
throw new Error(result.stderr.trim() || "Not a git repository");
|
|
18813
19150
|
}
|
|
18814
|
-
|
|
18815
|
-
|
|
18816
|
-
|
|
18817
|
-
|
|
18818
|
-
|
|
19151
|
+
} else {
|
|
19152
|
+
gitRoot = await new Promise((resolveRoot, reject) => {
|
|
19153
|
+
const proc = spawnProcess("git", ["rev-parse", "--show-toplevel"], { cwd });
|
|
19154
|
+
let out = "";
|
|
19155
|
+
let err = "";
|
|
19156
|
+
if (proc.stdout) {
|
|
19157
|
+
proc.stdout.on("data", (d) => {
|
|
19158
|
+
out += d.toString();
|
|
19159
|
+
});
|
|
19160
|
+
}
|
|
19161
|
+
if (proc.stderr) {
|
|
19162
|
+
proc.stderr.on("data", (d) => {
|
|
19163
|
+
err += d.toString();
|
|
19164
|
+
});
|
|
18819
19165
|
}
|
|
19166
|
+
proc.on("close", (code) => {
|
|
19167
|
+
if (code === 0) {
|
|
19168
|
+
resolveRoot(out.trim());
|
|
19169
|
+
} else {
|
|
19170
|
+
reject(new Error(err.trim() || "Not a git repository"));
|
|
19171
|
+
}
|
|
19172
|
+
});
|
|
19173
|
+
proc.on("error", reject);
|
|
18820
19174
|
});
|
|
18821
|
-
|
|
18822
|
-
|
|
19175
|
+
}
|
|
19176
|
+
gitRootCache.set(cacheKey, gitRoot);
|
|
19177
|
+
return gitRoot;
|
|
18823
19178
|
}
|
|
18824
|
-
function getCurrentBranch(gitRoot) {
|
|
19179
|
+
async function getCurrentBranch(gitRoot) {
|
|
19180
|
+
if (isGitSubprocessTimingEnabled()) {
|
|
19181
|
+
const result = await runGitCommandTimed("getCurrentBranch", gitRoot, [
|
|
19182
|
+
"rev-parse",
|
|
19183
|
+
"--abbrev-ref",
|
|
19184
|
+
"HEAD"
|
|
19185
|
+
]);
|
|
19186
|
+
return result.stdout.trim();
|
|
19187
|
+
}
|
|
18825
19188
|
return new Promise((resolve6) => {
|
|
18826
19189
|
const proc = spawnProcess("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitRoot });
|
|
18827
19190
|
let out = "";
|
|
@@ -18934,7 +19297,15 @@ new file mode 100644
|
|
|
18934
19297
|
}
|
|
18935
19298
|
return parts.join("\n");
|
|
18936
19299
|
}
|
|
18937
|
-
function getGitStatus(gitRoot) {
|
|
19300
|
+
async function getGitStatus(gitRoot) {
|
|
19301
|
+
if (isGitSubprocessTimingEnabled()) {
|
|
19302
|
+
const result = await runGitCommandTimed("getGitStatus", gitRoot, [
|
|
19303
|
+
"status",
|
|
19304
|
+
"--porcelain",
|
|
19305
|
+
"--untracked-files=all"
|
|
19306
|
+
]);
|
|
19307
|
+
return result.stdout;
|
|
19308
|
+
}
|
|
18938
19309
|
return new Promise((resolve6) => {
|
|
18939
19310
|
const proc = spawnProcess("git", ["status", "--porcelain", "--untracked-files=all"], {
|
|
18940
19311
|
cwd: gitRoot
|
|
@@ -18987,7 +19358,18 @@ function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
|
|
|
18987
19358
|
proc.on("error", () => resolve6(false));
|
|
18988
19359
|
});
|
|
18989
19360
|
}
|
|
18990
|
-
function getRemoteOrigin(gitRoot) {
|
|
19361
|
+
async function getRemoteOrigin(gitRoot) {
|
|
19362
|
+
if (isGitSubprocessTimingEnabled()) {
|
|
19363
|
+
const result = await runGitCommandTimed("getRemoteOrigin", gitRoot, [
|
|
19364
|
+
"remote",
|
|
19365
|
+
"get-url",
|
|
19366
|
+
"origin"
|
|
19367
|
+
]);
|
|
19368
|
+
if (result.code === 0) {
|
|
19369
|
+
return result.stdout.trim();
|
|
19370
|
+
}
|
|
19371
|
+
return "";
|
|
19372
|
+
}
|
|
18991
19373
|
return new Promise((resolve6) => {
|
|
18992
19374
|
const proc = spawnProcess("git", ["remote", "get-url", "origin"], { cwd: gitRoot });
|
|
18993
19375
|
let out = "";
|
|
@@ -19006,54 +19388,22 @@ function getRemoteOrigin(gitRoot) {
|
|
|
19006
19388
|
proc.on("error", () => resolve6(""));
|
|
19007
19389
|
});
|
|
19008
19390
|
}
|
|
19009
|
-
async function getGitSyncStatus(gitRoot, branch) {
|
|
19391
|
+
async function getGitSyncStatus(gitRoot, branch, options) {
|
|
19392
|
+
const fetchRemote = options?.fetchRemote ?? false;
|
|
19010
19393
|
console.log(`[git-status] Checking sync status for branch: ${branch}`);
|
|
19011
|
-
|
|
19012
|
-
|
|
19013
|
-
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
fetchProc.on("error", () => {
|
|
19019
|
-
console.log(`[git-status] Fetch error (continuing anyway)`);
|
|
19020
|
-
resolve6();
|
|
19021
|
-
});
|
|
19022
|
-
});
|
|
19023
|
-
console.log(`[git-status] Finding default branch...`);
|
|
19024
|
-
const defaultBranch = await findDefaultBranch(gitRoot);
|
|
19025
|
-
console.log(`[git-status] Default branch: ${defaultBranch}`);
|
|
19026
|
-
if (defaultBranch && defaultBranch !== branch) {
|
|
19027
|
-
console.log(`[git-status] Checking if ${branch} is behind ${defaultBranch}...`);
|
|
19028
|
-
const isBehindDefault = await new Promise((resolve6) => {
|
|
19029
|
-
const proc = spawnProcess(
|
|
19030
|
-
"git",
|
|
19031
|
-
["rev-list", "--count", `${branch}..origin/${defaultBranch}`],
|
|
19032
|
-
{ cwd: gitRoot }
|
|
19033
|
-
);
|
|
19034
|
-
let out = "";
|
|
19035
|
-
if (proc.stdout) {
|
|
19036
|
-
proc.stdout.on("data", (d) => {
|
|
19037
|
-
out += d.toString();
|
|
19038
|
-
});
|
|
19039
|
-
}
|
|
19040
|
-
proc.on("close", () => {
|
|
19041
|
-
const count = parseInt(out.trim(), 10);
|
|
19042
|
-
console.log(`[git-status] Commits in ${defaultBranch} but not in ${branch}: ${count}`);
|
|
19043
|
-
resolve6(count > 0);
|
|
19394
|
+
if (fetchRemote) {
|
|
19395
|
+
console.log(`[git-status] Fetching from origin...`);
|
|
19396
|
+
await new Promise((resolve6) => {
|
|
19397
|
+
const fetchProc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
|
|
19398
|
+
fetchProc.on("close", () => {
|
|
19399
|
+
console.log(`[git-status] \u2713 Fetch completed`);
|
|
19400
|
+
resolve6();
|
|
19044
19401
|
});
|
|
19045
|
-
|
|
19046
|
-
console.log(`[git-status]
|
|
19047
|
-
resolve6(
|
|
19402
|
+
fetchProc.on("error", () => {
|
|
19403
|
+
console.log(`[git-status] Fetch error (continuing anyway)`);
|
|
19404
|
+
resolve6();
|
|
19048
19405
|
});
|
|
19049
19406
|
});
|
|
19050
|
-
if (isBehindDefault) {
|
|
19051
|
-
console.log(`[git-status] \u2713 Branch IS behind default branch`);
|
|
19052
|
-
return "Behind";
|
|
19053
|
-
}
|
|
19054
|
-
console.log(`[git-status] Branch is NOT behind default branch`);
|
|
19055
|
-
} else {
|
|
19056
|
-
console.log(`[git-status] Skipping default branch check (same branch or no default found)`);
|
|
19057
19407
|
}
|
|
19058
19408
|
console.log(`[git-status] Checking status against remote tracking branch...`);
|
|
19059
19409
|
return new Promise((resolve6) => {
|
|
@@ -19071,27 +19421,30 @@ async function getGitSyncStatus(gitRoot, branch) {
|
|
|
19071
19421
|
});
|
|
19072
19422
|
}
|
|
19073
19423
|
statusProc.on("close", () => {
|
|
19074
|
-
const output = statusOut + statusErr;
|
|
19075
|
-
console.log(`[git-status] git status -sb output: ${output
|
|
19076
|
-
|
|
19424
|
+
const output = (statusOut + statusErr).trim();
|
|
19425
|
+
console.log(`[git-status] git status -sb output: ${output}`);
|
|
19426
|
+
const firstLine = output.split("\n")[0] ?? "";
|
|
19427
|
+
if (!firstLine.includes(branch)) {
|
|
19077
19428
|
console.log(`[git-status] Branch name not found in status output, returning 'Up to date'`);
|
|
19078
19429
|
resolve6("Up to date");
|
|
19079
19430
|
return;
|
|
19080
19431
|
}
|
|
19081
|
-
if (
|
|
19432
|
+
if (!firstLine.includes("...")) {
|
|
19433
|
+
console.log(`[git-status] No upstream tracking branch detected, returning 'Up to date'`);
|
|
19434
|
+
resolve6("Up to date");
|
|
19435
|
+
return;
|
|
19436
|
+
}
|
|
19437
|
+
if (firstLine.includes("ahead") && firstLine.includes("behind")) {
|
|
19082
19438
|
console.log(`[git-status] \u2713 Status: Diverged`);
|
|
19083
19439
|
resolve6("Diverged");
|
|
19084
|
-
} else if (
|
|
19440
|
+
} else if (firstLine.includes("behind")) {
|
|
19085
19441
|
console.log(`[git-status] \u2713 Status: Behind`);
|
|
19086
19442
|
resolve6("Behind");
|
|
19087
|
-
} else if (
|
|
19443
|
+
} else if (firstLine.includes("ahead")) {
|
|
19088
19444
|
console.log(`[git-status] \u2713 Status: Ahead`);
|
|
19089
19445
|
resolve6("Ahead");
|
|
19090
|
-
} else if (output.includes("#") && !output.includes("ahead") && !output.includes("behind")) {
|
|
19091
|
-
console.log(`[git-status] \u2713 Status: Up to date`);
|
|
19092
|
-
resolve6("Up to date");
|
|
19093
19446
|
} else {
|
|
19094
|
-
console.log(`[git-status] \u2713 Status: Up to date
|
|
19447
|
+
console.log(`[git-status] \u2713 Status: Up to date`);
|
|
19095
19448
|
resolve6("Up to date");
|
|
19096
19449
|
}
|
|
19097
19450
|
});
|
|
@@ -19137,18 +19490,13 @@ function getDefaultBranch(gitRoot) {
|
|
|
19137
19490
|
});
|
|
19138
19491
|
}
|
|
19139
19492
|
async function findDefaultBranch(gitRoot) {
|
|
19493
|
+
const fromOriginHead = await getDefaultBranch(gitRoot);
|
|
19494
|
+
if (fromOriginHead) return fromOriginHead;
|
|
19140
19495
|
const candidates = ["main", "master", "develop", "staging"];
|
|
19141
|
-
|
|
19142
|
-
|
|
19143
|
-
|
|
19144
|
-
|
|
19145
|
-
});
|
|
19146
|
-
proc.on("close", (code) => resolve6(code === 0));
|
|
19147
|
-
proc.on("error", () => resolve6(false));
|
|
19148
|
-
});
|
|
19149
|
-
if (exists) return branch;
|
|
19150
|
-
}
|
|
19151
|
-
return null;
|
|
19496
|
+
const checks = await Promise.all(
|
|
19497
|
+
candidates.map(async (branch) => await hasRemoteBranch(gitRoot, branch) ? branch : null)
|
|
19498
|
+
);
|
|
19499
|
+
return checks.find((branch) => branch !== null) ?? null;
|
|
19152
19500
|
}
|
|
19153
19501
|
function getCommitLog(gitRoot, baseBranch, currentBranch) {
|
|
19154
19502
|
return new Promise((resolve6) => {
|
|
@@ -19394,7 +19742,31 @@ function createPullRequest(gitRoot, title, description) {
|
|
|
19394
19742
|
});
|
|
19395
19743
|
});
|
|
19396
19744
|
}
|
|
19397
|
-
function getPullRequestStatus(gitRoot) {
|
|
19745
|
+
async function getPullRequestStatus(gitRoot) {
|
|
19746
|
+
if (isGitSubprocessTimingEnabled()) {
|
|
19747
|
+
const result = await runCommandTimed(
|
|
19748
|
+
"getPullRequestStatus",
|
|
19749
|
+
"gh",
|
|
19750
|
+
["pr", "view", "--json", "url,state"],
|
|
19751
|
+
gitRoot
|
|
19752
|
+
);
|
|
19753
|
+
if (result.code === 0) {
|
|
19754
|
+
try {
|
|
19755
|
+
const prData = JSON.parse(result.stdout.trim());
|
|
19756
|
+
return {
|
|
19757
|
+
exists: true,
|
|
19758
|
+
url: prData.url,
|
|
19759
|
+
state: prData.state
|
|
19760
|
+
};
|
|
19761
|
+
} catch {
|
|
19762
|
+
throw new Error("Failed to parse PR data");
|
|
19763
|
+
}
|
|
19764
|
+
}
|
|
19765
|
+
if (result.stderr.includes("no pull requests") || result.stderr.includes("not found")) {
|
|
19766
|
+
return { exists: false };
|
|
19767
|
+
}
|
|
19768
|
+
throw new Error(result.stderr || "Failed to check PR status");
|
|
19769
|
+
}
|
|
19398
19770
|
return new Promise((resolve6, reject) => {
|
|
19399
19771
|
const args2 = ["pr", "view", "--json", "url,state"];
|
|
19400
19772
|
const proc = spawnProcess("gh", args2, { cwd: gitRoot });
|
|
@@ -20160,7 +20532,60 @@ async function gitGithubStatusHandler(c, metadataManager) {
|
|
|
20160
20532
|
|
|
20161
20533
|
// src/features/git/git-unified-status.route.ts
|
|
20162
20534
|
import { existsSync as existsSync20 } from "fs";
|
|
20163
|
-
|
|
20535
|
+
function parseChangedFiles(statusOutput) {
|
|
20536
|
+
const lines = statusOutput.trim().split("\n").filter((line) => line.length > 0);
|
|
20537
|
+
return {
|
|
20538
|
+
hasChanges: lines.length > 0,
|
|
20539
|
+
changedFilesCount: lines.length
|
|
20540
|
+
};
|
|
20541
|
+
}
|
|
20542
|
+
function buildRepoUrl(remoteUrl, isOnGitHub) {
|
|
20543
|
+
if (!isOnGitHub) return "";
|
|
20544
|
+
if (remoteUrl.startsWith("https://")) {
|
|
20545
|
+
return remoteUrl.replace(/\.git$/, "");
|
|
20546
|
+
}
|
|
20547
|
+
if (remoteUrl.startsWith("git@")) {
|
|
20548
|
+
const match = remoteUrl.match(/git@github\.com:(.+?)\.git$/);
|
|
20549
|
+
if (match) {
|
|
20550
|
+
return `https://github.com/${match[1]}`;
|
|
20551
|
+
}
|
|
20552
|
+
}
|
|
20553
|
+
return "";
|
|
20554
|
+
}
|
|
20555
|
+
function createServerStepTracker(threadId) {
|
|
20556
|
+
const steps = [];
|
|
20557
|
+
const requestStart = Date.now();
|
|
20558
|
+
async function step(name, fn) {
|
|
20559
|
+
const start = Date.now();
|
|
20560
|
+
try {
|
|
20561
|
+
return await fn();
|
|
20562
|
+
} finally {
|
|
20563
|
+
const ms = Date.now() - start;
|
|
20564
|
+
steps.push({ step: name, ms });
|
|
20565
|
+
console.log(`[gitops-timing] unified-status thread=${threadId} step=${name} ${ms}ms`);
|
|
20566
|
+
}
|
|
20567
|
+
}
|
|
20568
|
+
function record(name, start, extra) {
|
|
20569
|
+
const ms = Date.now() - start;
|
|
20570
|
+
steps.push({ step: name, ms });
|
|
20571
|
+
console.log(
|
|
20572
|
+
`[gitops-timing] unified-status thread=${threadId} step=${name} ${ms}ms${extra ? ` ${extra}` : ""}`
|
|
20573
|
+
);
|
|
20574
|
+
}
|
|
20575
|
+
function summary(extra) {
|
|
20576
|
+
const total = Date.now() - requestStart;
|
|
20577
|
+
const slowest = steps.reduce((best, current) => current.ms >= best.ms ? current : best, {
|
|
20578
|
+
step: "none",
|
|
20579
|
+
ms: 0
|
|
20580
|
+
});
|
|
20581
|
+
const breakdown = steps.map((s) => `${s.step}:${s.ms}`).join(" ");
|
|
20582
|
+
console.log(
|
|
20583
|
+
`[gitops-timing] unified-status thread=${threadId} summary total=${total}ms slowest=${slowest.step}@${slowest.ms}ms breakdown=[${breakdown}]${extra ? ` ${extra}` : ""}`
|
|
20584
|
+
);
|
|
20585
|
+
}
|
|
20586
|
+
return { step, record, summary };
|
|
20587
|
+
}
|
|
20588
|
+
async function gitUnifiedStatusHandler(c, _metadataManager, db) {
|
|
20164
20589
|
try {
|
|
20165
20590
|
const threadId = c.req.param("threadId");
|
|
20166
20591
|
if (!threadId) {
|
|
@@ -20172,138 +20597,111 @@ async function gitUnifiedStatusHandler(c, metadataManager, db) {
|
|
|
20172
20597
|
return c.json(response, statusCode);
|
|
20173
20598
|
}
|
|
20174
20599
|
const fresh = c.req.query("fresh") === "true";
|
|
20600
|
+
const tracker = createServerStepTracker(threadId);
|
|
20175
20601
|
if (!fresh) {
|
|
20602
|
+
const cacheStart = Date.now();
|
|
20176
20603
|
const cached = await getGitStatusCache(db, threadId);
|
|
20604
|
+
tracker.record("db-cache-read", cacheStart, `hit=${cached ? "true" : "false"}`);
|
|
20177
20605
|
if (cached) {
|
|
20606
|
+
tracker.summary("cached=true");
|
|
20178
20607
|
return c.json({ ...cached, cached: true });
|
|
20179
20608
|
}
|
|
20180
20609
|
}
|
|
20181
|
-
|
|
20182
|
-
|
|
20183
|
-
return c.json({ error: "Thread not found" }, 404);
|
|
20184
|
-
}
|
|
20185
|
-
const repoPath = thread.path;
|
|
20186
|
-
if (!repoPath) {
|
|
20187
|
-
return c.json({ error: "Thread path not found" }, 404);
|
|
20188
|
-
}
|
|
20189
|
-
const absolutePath = resolveThreadPath(repoPath);
|
|
20190
|
-
if (!existsSync20(absolutePath)) {
|
|
20191
|
-
return c.json(
|
|
20192
|
-
{
|
|
20193
|
-
error: `Thread repo path does not exist: ${absolutePath}. Check that the project folder is present.`
|
|
20194
|
-
},
|
|
20195
|
-
400
|
|
20196
|
-
);
|
|
20197
|
-
}
|
|
20198
|
-
let gitRoot;
|
|
20199
|
-
try {
|
|
20200
|
-
gitRoot = await getGitRoot(absolutePath);
|
|
20201
|
-
} catch (e) {
|
|
20202
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
20203
|
-
return c.json(
|
|
20204
|
-
{
|
|
20205
|
-
error: `Path is not a git repository: ${absolutePath}. ${msg}`
|
|
20206
|
-
},
|
|
20207
|
-
400
|
|
20208
|
-
);
|
|
20209
|
-
}
|
|
20210
|
-
const { hasChanges, changedFilesCount } = await new Promise((resolve6) => {
|
|
20211
|
-
getGitStatus(gitRoot).then((statusOutput) => {
|
|
20212
|
-
const lines = statusOutput.trim().split("\n").filter((line) => line.length > 0);
|
|
20213
|
-
resolve6({
|
|
20214
|
-
hasChanges: lines.length > 0,
|
|
20215
|
-
changedFilesCount: lines.length
|
|
20216
|
-
});
|
|
20217
|
-
}).catch(() => resolve6({ hasChanges: false, changedFilesCount: 0 }));
|
|
20218
|
-
});
|
|
20219
|
-
const currentBranch = await getCurrentBranch(gitRoot);
|
|
20220
|
-
const hasUnpushed = await hasUnpushedCommits(gitRoot, currentBranch);
|
|
20221
|
-
const remoteUrl = await getRemoteOrigin(gitRoot);
|
|
20222
|
-
const isOnGitHub = remoteUrl.includes("github.com");
|
|
20223
|
-
let repoUrl = "";
|
|
20224
|
-
if (isOnGitHub) {
|
|
20225
|
-
if (remoteUrl.startsWith("https://")) {
|
|
20226
|
-
repoUrl = remoteUrl.replace(/\.git$/, "");
|
|
20227
|
-
} else if (remoteUrl.startsWith("git@")) {
|
|
20228
|
-
const match = remoteUrl.match(/git@github\.com:(.+?)\.git$/);
|
|
20229
|
-
if (match) {
|
|
20230
|
-
repoUrl = `https://github.com/${match[1]}`;
|
|
20231
|
-
}
|
|
20232
|
-
}
|
|
20233
|
-
}
|
|
20234
|
-
let hasRemoteBranchExists = false;
|
|
20235
|
-
if (remoteUrl && currentBranch && currentBranch !== "HEAD") {
|
|
20236
|
-
try {
|
|
20237
|
-
hasRemoteBranchExists = await hasRemoteBranch(gitRoot, currentBranch);
|
|
20238
|
-
} catch (error) {
|
|
20239
|
-
console.warn(
|
|
20240
|
-
`Failed to check remote branch existence: ${error instanceof Error ? error.message : String(error)}`
|
|
20241
|
-
);
|
|
20242
|
-
hasRemoteBranchExists = false;
|
|
20243
|
-
}
|
|
20244
|
-
}
|
|
20245
|
-
let defaultBranchName = null;
|
|
20246
|
-
try {
|
|
20247
|
-
defaultBranchName = await findDefaultBranch(gitRoot);
|
|
20248
|
-
} catch (error) {
|
|
20249
|
-
console.warn(
|
|
20250
|
-
`Failed to find default branch: ${error instanceof Error ? error.message : String(error)}`
|
|
20251
|
-
);
|
|
20252
|
-
}
|
|
20253
|
-
let commitsAheadOfDefault = false;
|
|
20254
|
-
if (defaultBranchName && currentBranch && currentBranch !== defaultBranchName) {
|
|
20255
|
-
try {
|
|
20256
|
-
commitsAheadOfDefault = await hasCommitsAheadOfDefault(gitRoot, defaultBranchName);
|
|
20257
|
-
} catch {
|
|
20258
|
-
commitsAheadOfDefault = false;
|
|
20259
|
-
}
|
|
20260
|
-
}
|
|
20261
|
-
let status = "Up to date";
|
|
20262
|
-
if (isOnGitHub && remoteUrl) {
|
|
20610
|
+
return withGitTimingContext(threadId, async () => {
|
|
20611
|
+
const stopLagMonitor = startEventLoopLagMonitor(threadId);
|
|
20263
20612
|
try {
|
|
20264
|
-
|
|
20265
|
-
|
|
20613
|
+
const thread = await tracker.step("thread-lookup", () => getThread(db, threadId));
|
|
20614
|
+
if (!thread) {
|
|
20615
|
+
tracker.summary("error=thread-not-found");
|
|
20616
|
+
return c.json({ error: "Thread not found" }, 404);
|
|
20266
20617
|
}
|
|
20267
|
-
|
|
20268
|
-
|
|
20269
|
-
|
|
20270
|
-
|
|
20271
|
-
|
|
20272
|
-
|
|
20273
|
-
|
|
20274
|
-
|
|
20275
|
-
|
|
20276
|
-
|
|
20277
|
-
|
|
20278
|
-
|
|
20279
|
-
|
|
20280
|
-
|
|
20618
|
+
const repoPath = thread.path;
|
|
20619
|
+
if (!repoPath) {
|
|
20620
|
+
tracker.summary("error=thread-path-missing");
|
|
20621
|
+
return c.json({ error: "Thread path not found" }, 404);
|
|
20622
|
+
}
|
|
20623
|
+
const absolutePath = resolveThreadPath(repoPath);
|
|
20624
|
+
if (!existsSync20(absolutePath)) {
|
|
20625
|
+
tracker.summary("error=repo-path-missing");
|
|
20626
|
+
return c.json(
|
|
20627
|
+
{
|
|
20628
|
+
error: `Thread repo path does not exist: ${absolutePath}. Check that the project folder is present.`
|
|
20629
|
+
},
|
|
20630
|
+
400
|
|
20631
|
+
);
|
|
20632
|
+
}
|
|
20633
|
+
let gitRoot;
|
|
20634
|
+
try {
|
|
20635
|
+
gitRoot = await tracker.step("resolve-git-root", () => getGitRoot(absolutePath));
|
|
20636
|
+
} catch (e) {
|
|
20637
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
20638
|
+
tracker.summary("error=not-git-repo");
|
|
20639
|
+
return c.json(
|
|
20640
|
+
{
|
|
20641
|
+
error: `Path is not a git repository: ${absolutePath}. ${msg}`
|
|
20642
|
+
},
|
|
20643
|
+
400
|
|
20644
|
+
);
|
|
20645
|
+
}
|
|
20646
|
+
const [statusOutput, currentBranch, remoteUrl] = await Promise.all([
|
|
20647
|
+
tracker.step("getGitStatus", () => getGitStatus(gitRoot).catch(() => "")),
|
|
20648
|
+
tracker.step("getCurrentBranch", () => getCurrentBranch(gitRoot)),
|
|
20649
|
+
tracker.step("getRemoteOrigin", () => getRemoteOrigin(gitRoot))
|
|
20650
|
+
]);
|
|
20651
|
+
const { hasChanges, changedFilesCount } = parseChangedFiles(statusOutput);
|
|
20652
|
+
const isOnGitHub = remoteUrl.includes("github.com");
|
|
20653
|
+
const repoUrl = buildRepoUrl(remoteUrl, isOnGitHub);
|
|
20654
|
+
const [hasUnpushed, hasRemoteBranchExists, defaultBranchName] = await Promise.all([
|
|
20655
|
+
tracker.step("hasUnpushedCommits", () => hasUnpushedCommits(gitRoot, currentBranch)),
|
|
20656
|
+
remoteUrl && currentBranch && currentBranch !== "HEAD" ? tracker.step(
|
|
20657
|
+
"hasRemoteBranch",
|
|
20658
|
+
() => hasRemoteBranch(gitRoot, currentBranch).catch(() => false)
|
|
20659
|
+
) : Promise.resolve(false),
|
|
20660
|
+
tracker.step("findDefaultBranch", () => findDefaultBranch(gitRoot).catch(() => null))
|
|
20661
|
+
]);
|
|
20662
|
+
const emptyPrStatus = { exists: false };
|
|
20663
|
+
const [commitsAheadOfDefault, status, prStatus] = await Promise.all([
|
|
20664
|
+
defaultBranchName && currentBranch && currentBranch !== defaultBranchName ? tracker.step(
|
|
20665
|
+
"hasCommitsAheadOfDefault",
|
|
20666
|
+
() => hasCommitsAheadOfDefault(gitRoot, defaultBranchName).catch(() => false)
|
|
20667
|
+
) : Promise.resolve(false),
|
|
20668
|
+
isOnGitHub && remoteUrl && currentBranch && currentBranch !== "HEAD" ? tracker.step(
|
|
20669
|
+
"getGitSyncStatus",
|
|
20670
|
+
() => getGitSyncStatus(gitRoot, currentBranch, { fetchRemote: fresh }).catch(
|
|
20671
|
+
() => "Up to date"
|
|
20672
|
+
)
|
|
20673
|
+
) : Promise.resolve("Up to date"),
|
|
20674
|
+
isOnGitHub ? tracker.step(
|
|
20675
|
+
"getPullRequestStatus",
|
|
20676
|
+
() => getPullRequestStatus(gitRoot).catch(() => emptyPrStatus)
|
|
20677
|
+
) : Promise.resolve(emptyPrStatus)
|
|
20678
|
+
]);
|
|
20679
|
+
const result = {
|
|
20680
|
+
hasChanges,
|
|
20681
|
+
changedFilesCount,
|
|
20682
|
+
currentBranch,
|
|
20683
|
+
hasUnpushedCommits: hasUnpushed,
|
|
20684
|
+
hasRemoteBranch: hasRemoteBranchExists,
|
|
20685
|
+
commitsAheadOfDefault,
|
|
20686
|
+
isOnGitHub,
|
|
20687
|
+
remoteUrl,
|
|
20688
|
+
canCreateRepo: !isOnGitHub && remoteUrl.length === 0,
|
|
20689
|
+
repoUrl,
|
|
20690
|
+
status,
|
|
20691
|
+
defaultBranch: defaultBranchName,
|
|
20692
|
+
prExists: prStatus.exists,
|
|
20693
|
+
prUrl: prStatus.url,
|
|
20694
|
+
prState: prStatus.state
|
|
20695
|
+
};
|
|
20696
|
+
await tracker.step("db-cache-write", () => setGitStatusCache(db, threadId, result));
|
|
20697
|
+
tracker.summary(
|
|
20698
|
+
`cached=false branch=${currentBranch || "none"} changes=${changedFilesCount} prExists=${prStatus.exists} fresh=${fresh}`
|
|
20281
20699
|
);
|
|
20282
|
-
|
|
20700
|
+
return c.json({ ...result, cached: false });
|
|
20701
|
+
} finally {
|
|
20702
|
+
stopLagMonitor();
|
|
20283
20703
|
}
|
|
20284
|
-
}
|
|
20285
|
-
const result = {
|
|
20286
|
-
// Git status fields
|
|
20287
|
-
hasChanges,
|
|
20288
|
-
changedFilesCount,
|
|
20289
|
-
currentBranch,
|
|
20290
|
-
hasUnpushedCommits: hasUnpushed,
|
|
20291
|
-
hasRemoteBranch: hasRemoteBranchExists,
|
|
20292
|
-
commitsAheadOfDefault,
|
|
20293
|
-
// GitHub status fields
|
|
20294
|
-
isOnGitHub,
|
|
20295
|
-
remoteUrl,
|
|
20296
|
-
canCreateRepo: !isOnGitHub && remoteUrl.length === 0,
|
|
20297
|
-
repoUrl,
|
|
20298
|
-
status,
|
|
20299
|
-
defaultBranch: defaultBranchName,
|
|
20300
|
-
// PR status fields
|
|
20301
|
-
prExists: prStatus.exists,
|
|
20302
|
-
prUrl: prStatus.url,
|
|
20303
|
-
prState: prStatus.state
|
|
20304
|
-
};
|
|
20305
|
-
await setGitStatusCache(db, threadId, result);
|
|
20306
|
-
return c.json({ ...result, cached: false });
|
|
20704
|
+
});
|
|
20307
20705
|
} catch (error) {
|
|
20308
20706
|
const message = error instanceof Error ? error.message : "Failed to get unified git status";
|
|
20309
20707
|
return c.json({ error: message }, 500);
|
|
@@ -22197,12 +22595,23 @@ function createBrowserJsRoutes(threadManager) {
|
|
|
22197
22595
|
|
|
22198
22596
|
// src/features/voice-model/voice-model.routes.ts
|
|
22199
22597
|
import { Hono as Hono21 } from "hono";
|
|
22200
|
-
var
|
|
22598
|
+
var VOICE_MODEL_URLS = {
|
|
22599
|
+
default: "https://install.tarsk.io/voice-models/ggml-tiny.en.bin",
|
|
22600
|
+
tiny: "https://install.tarsk.io/voice-models/ggml-tiny.en-q5_1.bin"
|
|
22601
|
+
};
|
|
22602
|
+
function getVoiceModelUrl(model) {
|
|
22603
|
+
if (model === "tiny") {
|
|
22604
|
+
return VOICE_MODEL_URLS.tiny;
|
|
22605
|
+
}
|
|
22606
|
+
return VOICE_MODEL_URLS.default;
|
|
22607
|
+
}
|
|
22201
22608
|
function createVoiceModelRoutes() {
|
|
22202
22609
|
const router = new Hono21();
|
|
22203
22610
|
router.get("/download", async (c) => {
|
|
22611
|
+
const selectedModel = c.req.query("model");
|
|
22612
|
+
const modelUrl = getVoiceModelUrl(selectedModel);
|
|
22204
22613
|
try {
|
|
22205
|
-
const response = await fetch(
|
|
22614
|
+
const response = await fetch(modelUrl);
|
|
22206
22615
|
if (!response.ok) {
|
|
22207
22616
|
return errorResponse(
|
|
22208
22617
|
c,
|
|
@@ -22213,7 +22622,8 @@ function createVoiceModelRoutes() {
|
|
|
22213
22622
|
}
|
|
22214
22623
|
const contentLength = response.headers.get("content-length");
|
|
22215
22624
|
const headers = {
|
|
22216
|
-
"Content-Type": "application/octet-stream"
|
|
22625
|
+
"Content-Type": "application/octet-stream",
|
|
22626
|
+
"X-Tarsk-Voice-Model": selectedModel === "tiny" ? "tiny" : "default"
|
|
22217
22627
|
};
|
|
22218
22628
|
if (contentLength) {
|
|
22219
22629
|
headers["Content-Length"] = contentLength;
|
|
@@ -22275,13 +22685,82 @@ function createLogsRoutes() {
|
|
|
22275
22685
|
return router;
|
|
22276
22686
|
}
|
|
22277
22687
|
|
|
22688
|
+
// src/features/user-tasks/user-tasks.routes.ts
|
|
22689
|
+
init_database();
|
|
22690
|
+
import { Hono as Hono23 } from "hono";
|
|
22691
|
+
|
|
22692
|
+
// src/features/user-tasks/user-tasks.database.ts
|
|
22693
|
+
init_database();
|
|
22694
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
22695
|
+
async function getUserTasksByThread(db, threadId) {
|
|
22696
|
+
const result = await db.execute({
|
|
22697
|
+
sql: `SELECT id, threadId, content, createdAt, updatedAt
|
|
22698
|
+
FROM user_tasks
|
|
22699
|
+
WHERE threadId = ?
|
|
22700
|
+
ORDER BY createdAt ASC`,
|
|
22701
|
+
args: [threadId]
|
|
22702
|
+
});
|
|
22703
|
+
return result.rows;
|
|
22704
|
+
}
|
|
22705
|
+
async function insertUserTask(threadId, content) {
|
|
22706
|
+
const db = await getDatabase();
|
|
22707
|
+
const id = randomUUID11();
|
|
22708
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22709
|
+
await db.execute({
|
|
22710
|
+
sql: `INSERT INTO user_tasks (id, threadId, content, createdAt, updatedAt)
|
|
22711
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
22712
|
+
args: [id, threadId, content, now, now]
|
|
22713
|
+
});
|
|
22714
|
+
return {
|
|
22715
|
+
id,
|
|
22716
|
+
threadId,
|
|
22717
|
+
content,
|
|
22718
|
+
createdAt: now,
|
|
22719
|
+
updatedAt: now
|
|
22720
|
+
};
|
|
22721
|
+
}
|
|
22722
|
+
async function deleteUserTaskById(db, id) {
|
|
22723
|
+
await db.execute({
|
|
22724
|
+
sql: `DELETE FROM user_tasks WHERE id = ?`,
|
|
22725
|
+
args: [id]
|
|
22726
|
+
});
|
|
22727
|
+
}
|
|
22728
|
+
|
|
22729
|
+
// src/features/user-tasks/user-tasks.routes.ts
|
|
22730
|
+
function createUserTaskRoutes() {
|
|
22731
|
+
const router = new Hono23();
|
|
22732
|
+
router.get("/:threadId/user-tasks", async (c) => {
|
|
22733
|
+
const threadId = c.req.param("threadId");
|
|
22734
|
+
const db = await getDatabase();
|
|
22735
|
+
const tasks = await getUserTasksByThread(db, threadId);
|
|
22736
|
+
return c.json({ tasks });
|
|
22737
|
+
});
|
|
22738
|
+
router.post("/:threadId/user-tasks", async (c) => {
|
|
22739
|
+
const threadId = c.req.param("threadId");
|
|
22740
|
+
const body = await c.req.json();
|
|
22741
|
+
const content = body.content?.trim();
|
|
22742
|
+
if (!content) {
|
|
22743
|
+
return c.json({ error: "content is required" }, 400);
|
|
22744
|
+
}
|
|
22745
|
+
const task = await insertUserTask(threadId, content);
|
|
22746
|
+
return c.json(task, 201);
|
|
22747
|
+
});
|
|
22748
|
+
router.delete("/:threadId/user-tasks/:taskId", async (c) => {
|
|
22749
|
+
const taskId = c.req.param("taskId");
|
|
22750
|
+
const db = await getDatabase();
|
|
22751
|
+
await deleteUserTaskById(db, taskId);
|
|
22752
|
+
return c.json({ success: true });
|
|
22753
|
+
});
|
|
22754
|
+
return router;
|
|
22755
|
+
}
|
|
22756
|
+
|
|
22278
22757
|
// src/server.ts
|
|
22279
22758
|
var __filename = fileURLToPath(import.meta.url);
|
|
22280
22759
|
var __dirname = path4.dirname(__filename);
|
|
22281
22760
|
async function startTarskServer(options) {
|
|
22282
22761
|
const { isDebug: isDebug2, publicDir: publicDirOverride } = options;
|
|
22283
22762
|
const port = isDebug2 ? 462 : process.env.PORT ? parseInt(process.env.PORT) : 641;
|
|
22284
|
-
const app = new
|
|
22763
|
+
const app = new Hono24();
|
|
22285
22764
|
app.use("/*", cors());
|
|
22286
22765
|
app.use("/*", async (c, next) => {
|
|
22287
22766
|
c.header("Cross-Origin-Opener-Policy", "same-origin");
|
|
@@ -22341,6 +22820,7 @@ async function startTarskServer(options) {
|
|
|
22341
22820
|
app.route("/api/projects", createRunRoutes(projectManager));
|
|
22342
22821
|
app.route("/api/projects", createProjectTodosRoutes(metadataManager));
|
|
22343
22822
|
app.route("/api/threads", createThreadRoutes(threadManager, gitManager, conversationManager));
|
|
22823
|
+
app.route("/api/threads", createUserTaskRoutes());
|
|
22344
22824
|
app.route(
|
|
22345
22825
|
"/api/chat",
|
|
22346
22826
|
createChatRoutes(threadManager, agentExecutor, conversationManager, processingStateManager)
|
|
@@ -22376,19 +22856,18 @@ async function startTarskServer(options) {
|
|
|
22376
22856
|
`No static frontend assets found. Expected one of: ${prodPublicDir}, ${devCopiedPublicDir}, ${appDistDir}. Build the app first with \`cd ../app && bun run build\`.`
|
|
22377
22857
|
);
|
|
22378
22858
|
}
|
|
22379
|
-
const staticRoot = path4.relative(process.cwd(), resolvedPublicDir);
|
|
22380
22859
|
app.use("/*", async (c, next) => {
|
|
22381
22860
|
if (c.req.path.startsWith("/api/")) {
|
|
22382
22861
|
return next();
|
|
22383
22862
|
}
|
|
22384
|
-
return serveStatic({ root:
|
|
22863
|
+
return serveStatic({ root: resolvedPublicDir })(c, next);
|
|
22385
22864
|
});
|
|
22386
22865
|
app.get("*", async (c, next) => {
|
|
22387
22866
|
if (c.req.path.startsWith("/api/")) {
|
|
22388
22867
|
return next();
|
|
22389
22868
|
}
|
|
22390
22869
|
return serveStatic({
|
|
22391
|
-
path: path4.
|
|
22870
|
+
path: path4.join(resolvedPublicDir, "index.html")
|
|
22392
22871
|
})(c, next);
|
|
22393
22872
|
});
|
|
22394
22873
|
app.all("*", (c) => {
|