tarsk 0.5.82 → 0.5.83

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.
Files changed (119) hide show
  1. package/dist/index.js +1115 -236
  2. package/dist/public/assets/{account-view-CC_IRw14.js → account-view-CJyN7iRT.js} +1 -1
  3. package/dist/public/assets/{alert-dialog-CVooKafv.js → alert-dialog-Dq9tEk9t.js} +1 -1
  4. package/dist/public/assets/api-CX0sFbpL.js +2 -0
  5. package/dist/public/assets/{async-error-boundary-Did-c5sq.js → async-error-boundary-BS6v_PGo.js} +1 -1
  6. package/dist/public/assets/browser-tab-Cj4ztL1i.js +12 -0
  7. package/dist/public/assets/{button-DVIjvd2G.js → button-zYjzm__g.js} +1 -1
  8. package/dist/public/assets/{card-DaHPdrGh.js → card-CpchkvHM.js} +1 -1
  9. package/dist/public/assets/chat-dock-context-DQmDS2i8.js +1 -0
  10. package/dist/public/assets/{commit-dialog-29_bboof.js → commit-dialog-D6hchKSJ.js} +1 -1
  11. package/dist/public/assets/{commit-error-dialog-ASd71LX_.js → commit-error-dialog-f7e8Qzpk.js} +1 -1
  12. package/dist/public/assets/{commit-method-fields-Bylj8VjO.js → commit-method-fields-6Ag-XY8a.js} +1 -1
  13. package/dist/public/assets/conversation-history-view-Dd6vfz4F.js +1 -0
  14. package/dist/public/assets/create-project-dialog-DkxC6wM-.js +2 -0
  15. package/dist/public/assets/{create-repo-dialog-DKcWU21A.js → create-repo-dialog-Y8dcOitj.js} +1 -1
  16. package/dist/public/assets/{delete-confirm-dialog-C9CQzpYL.js → delete-confirm-dialog-DvJ5VuAy.js} +1 -1
  17. package/dist/public/assets/diff-view-L1jxq-Ig.js +4 -0
  18. package/dist/public/assets/{dist-VhqWx9-S.js → dist-B-I0i8QZ.js} +1 -1
  19. package/dist/public/assets/{dist-GRNTsriM.js → dist-B6u0s1jB.js} +1 -1
  20. package/dist/public/assets/{dist-BYYtqVYm.js → dist-BJaGHz0F.js} +1 -1
  21. package/dist/public/assets/{dist-DqKWH8sc.js → dist-BhdiICUV.js} +1 -1
  22. package/dist/public/assets/{dist-BVQgAd-x.js → dist-CPtOAqwP.js} +1 -1
  23. package/dist/public/assets/{dist-BI_HNW9c.js → dist-CX9ud_WC.js} +1 -1
  24. package/dist/public/assets/dist-FJzwWcMv.js +2 -0
  25. package/dist/public/assets/{dist-BKsOX8kk.js → dist-lnsB93_y.js} +1 -1
  26. package/dist/public/assets/{es2015-CxOm1WGv.js → es2015-D67vh4rt.js} +1 -1
  27. package/dist/public/assets/{explorer-monaco-editor-DMvtDzfX.js → explorer-monaco-editor-C9uTsYgh.js} +1 -1
  28. package/dist/public/assets/explorer-tab-view-BD924tmt.js +2 -0
  29. package/dist/public/assets/explorer-tree-Cn6lp8jD.js +1 -0
  30. package/dist/public/assets/explorer-view-5wUektaN.js +76 -0
  31. package/dist/public/assets/{git-history-dialog-C3tayM-K.js → git-history-dialog-CgH_0Ho0.js} +1 -1
  32. package/dist/public/assets/index-DCWejG57.css +1 -0
  33. package/dist/public/assets/index-DpDgcOE9.js +114 -0
  34. package/dist/public/assets/{lucide-react-C-dX1rVz.js → lucide-react-CMzSV2MP.js} +1 -1
  35. package/dist/public/assets/{model-list-controls-Yu77SyTe.js → model-list-controls-ueD5Yk6B.js} +1 -1
  36. package/dist/public/assets/onboarding-dialog-pDK_d92N.js +1 -0
  37. package/dist/public/assets/{pill-tabs-B_YP0wlG.js → pill-tabs-CVcpOO4f.js} +1 -1
  38. package/dist/public/assets/{progress-indicator-QgGbLfer.js → progress-indicator-_fEKurQg.js} +1 -1
  39. package/dist/public/assets/project-coding-view-CCT83rVv.js +1 -0
  40. package/dist/public/assets/{project-danger-zone-view-y2DDNkUE.js → project-danger-zone-view-CtOeSKHP.js} +1 -1
  41. package/dist/public/assets/{project-env-vars-view-BAiGaui1.js → project-env-vars-view-D8zFtC0r.js} +1 -1
  42. package/dist/public/assets/project-model-aliases-view-t1nijA2C.js +1 -0
  43. package/dist/public/assets/project-settings-view-C4kg3nDX.js +1 -0
  44. package/dist/public/assets/project-tools-view-DrwiTff5.js +1 -0
  45. package/dist/public/assets/providers-list-view-DXRbYpKr.js +1 -0
  46. package/dist/public/assets/pull-request-dialog-D4JEkCWC.js +1 -0
  47. package/dist/public/assets/{pull-with-changes-dialog-DqH8eFaG.js → pull-with-changes-dialog-Mrdm4wbB.js} +1 -1
  48. package/dist/public/assets/{push-before-pr-dialog-CxdsKSp0.js → push-before-pr-dialog-Ce8MMKT-.js} +1 -1
  49. package/dist/public/assets/query-keys-C2vEjHA1.js +1 -0
  50. package/dist/public/assets/{scroll-area-DnAXfBMQ.js → scroll-area-BTpvIsIG.js} +1 -1
  51. package/dist/public/assets/settings-general-tabs-view-Cg4sWryU.js +2 -0
  52. package/dist/public/assets/settings-general-view-Cl1qPgIX.js +1 -0
  53. package/dist/public/assets/{settings-list-CvCGue_y.js → settings-list-DajPoFxt.js} +1 -1
  54. package/dist/public/assets/settings-logs-view-C2SUOaIF.js +5 -0
  55. package/dist/public/assets/settings-mcp-servers-view-BuGFdY9u.js +5 -0
  56. package/dist/public/assets/settings-models-view-Dh8eZR6v.js +1 -0
  57. package/dist/public/assets/settings-plugins-view-CJvYXOb6.js +2 -0
  58. package/dist/public/assets/settings-providers-view-CxZnVxuu.js +2 -0
  59. package/dist/public/assets/settings-setup-view-0NvbMts_.js +2 -0
  60. package/dist/public/assets/settings-skills-view-DT6Ldvfn.js +2 -0
  61. package/dist/public/assets/{settings-slack-view-xWc3HZl5.js → settings-slack-view-DH7r2apv.js} +1 -1
  62. package/dist/public/assets/settings-slash-commands-view-DxlXk7eT.js +1 -0
  63. package/dist/public/assets/{settings-statistics-view-BhrL3BwB.js → settings-statistics-view-mlcoR26Q.js} +1 -1
  64. package/dist/public/assets/settings-storage-view-DuN42zRw.js +1 -0
  65. package/dist/public/assets/{settings-subagents-view-C1WIOO6x.js → settings-subagents-view-C1r3Jtpe.js} +2 -2
  66. package/dist/public/assets/{settings-system-prompt-view-b3IaVlp1.js → settings-system-prompt-view-DcbUbDfa.js} +1 -1
  67. package/dist/public/assets/{settings-tab-bar-cx4BrJIY.js → settings-tab-bar-T8v2_Dfk.js} +1 -1
  68. package/dist/public/assets/settings-view-Bv5QDh0H.js +2 -0
  69. package/dist/public/assets/{slug-utils-D5qfm2sn.js → slug-utils-DhQjuU2J.js} +1 -1
  70. package/dist/public/assets/{store-CEYCdhjW.js → store-DCnnNgwq.js} +3 -3
  71. package/dist/public/assets/{table-DHvBIex4.js → table-DuD2DtkR.js} +1 -1
  72. package/dist/public/assets/tasks-view-D_TwmLgM.js +1 -0
  73. package/dist/public/assets/{terminal-panel-2T2Kc4CN.js → terminal-panel-BmphDRj0.js} +3 -3
  74. package/dist/public/assets/{toggle-group-DaT4acSz.js → toggle-group-D0gDKZ9x.js} +1 -1
  75. package/dist/public/assets/{tooltip-Cqmpv-M5.js → tooltip-6qlOE6WT.js} +1 -1
  76. package/dist/public/assets/{use-active-dock-panel-instance-CuYvC5Us.js → use-active-dock-panel-instance-CDcYJhbD.js} +1 -1
  77. package/dist/public/assets/{use-git-check-CmtXehtR.js → use-git-check-CZxYYnQ8.js} +1 -1
  78. package/dist/public/assets/{use-installed-ai-files-Cdzy29ie.js → use-installed-ai-files-uH9h6W5v.js} +1 -1
  79. package/dist/public/assets/useBaseQuery-DqLwB2Sq.js +1 -0
  80. package/dist/public/assets/{utils-CWTBjUlE.js → utils-uGptTPGm.js} +1 -1
  81. package/dist/public/assets/{web-bYOmn95I.js → web-B5i03FJP.js} +1 -1
  82. package/dist/public/assets/{web-CsChMX5V.js → web-CutQ8Xsm.js} +1 -1
  83. package/dist/public/assets/{web-BlBW8SWJ.js → web-D8PJDFGq.js} +1 -1
  84. package/dist/public/assets/{web-CIGP6pa8.js → web-Us0pibUb.js} +1 -1
  85. package/dist/public/index.html +23 -22
  86. package/package.json +1 -1
  87. package/dist/public/assets/api-IMkL19mR.js +0 -2
  88. package/dist/public/assets/browser-tab-CjdUnNr2.js +0 -12
  89. package/dist/public/assets/chat-dock-context-BA8EIToZ.js +0 -1
  90. package/dist/public/assets/conversation-history-view-CXHmQEA6.js +0 -1
  91. package/dist/public/assets/create-project-dialog-CtEUqqAd.js +0 -2
  92. package/dist/public/assets/diff-view-eYW3Jliv.js +0 -4
  93. package/dist/public/assets/dist-Ch137qdo.js +0 -2
  94. package/dist/public/assets/explorer-tab-view-CPN5epzi.js +0 -2
  95. package/dist/public/assets/explorer-tree-EjBLSWxV.js +0 -1
  96. package/dist/public/assets/explorer-view-DZh1FGrc.js +0 -76
  97. package/dist/public/assets/index-C3E_66vw.js +0 -114
  98. package/dist/public/assets/index-D42A6A6A.css +0 -1
  99. package/dist/public/assets/onboarding-dialog-C3vpTe8y.js +0 -1
  100. package/dist/public/assets/project-coding-view-DXtPNlL1.js +0 -1
  101. package/dist/public/assets/project-model-aliases-view-z5KtzKTK.js +0 -1
  102. package/dist/public/assets/project-settings-view-CvNtaVMG.js +0 -1
  103. package/dist/public/assets/project-tools-view-D3j7nVpp.js +0 -1
  104. package/dist/public/assets/providers-list-view-BY11UIaf.js +0 -1
  105. package/dist/public/assets/pull-request-dialog-D7Tc-DZW.js +0 -1
  106. package/dist/public/assets/settings-general-tabs-view-YngdB7te.js +0 -2
  107. package/dist/public/assets/settings-general-view-BLovSe8Y.js +0 -1
  108. package/dist/public/assets/settings-logs-view-B2vFqetG.js +0 -5
  109. package/dist/public/assets/settings-mcp-servers-view-BiWEZyG-.js +0 -5
  110. package/dist/public/assets/settings-models-view-B7STNfBy.js +0 -1
  111. package/dist/public/assets/settings-plugins-view-D8wypO3V.js +0 -2
  112. package/dist/public/assets/settings-providers-view-C5x9gCg7.js +0 -2
  113. package/dist/public/assets/settings-setup-view-VuWyf9Ey.js +0 -2
  114. package/dist/public/assets/settings-skills-view-cu2mzI28.js +0 -2
  115. package/dist/public/assets/settings-slash-commands-view-C9slbf4z.js +0 -1
  116. package/dist/public/assets/settings-storage-view-y4GyGxD-.js +0 -1
  117. package/dist/public/assets/settings-view-C_ggbCIB.js +0 -2
  118. package/dist/public/assets/tasks-view-DFBi6JaW.js +0 -1
  119. package/dist/public/assets/useBaseQuery-D6c39Xb1.js +0 -1
package/dist/index.js CHANGED
@@ -1747,7 +1747,13 @@ var init_tool_display = __esm({
1747
1747
  activeDisplayName: "Reading Reference"
1748
1748
  },
1749
1749
  { name: "run_js", enabled: true, displayName: "Run JS", activeDisplayName: "Running JS" },
1750
- { name: "todo", enabled: true, displayName: "Todo", activeDisplayName: "Updating" },
1750
+ {
1751
+ name: "todo",
1752
+ enabled: true,
1753
+ defaultEnabled: false,
1754
+ displayName: "Todo",
1755
+ activeDisplayName: "Updating"
1756
+ },
1751
1757
  { name: "tasks", enabled: true, displayName: "Tasks", activeDisplayName: "Managing Tasks" },
1752
1758
  {
1753
1759
  name: "tool_search",
@@ -2202,11 +2208,11 @@ function parseProjectSettingsDocument(raw2) {
2202
2208
  };
2203
2209
  }
2204
2210
  function migrateDefaultOffTools(settings3, version3) {
2205
- if (version3 !== null && version3 >= 2) {
2211
+ if (version3 !== null && version3 >= 3) {
2206
2212
  return false;
2207
2213
  }
2208
2214
  const defaults3 = getDefaultOffToolNames();
2209
- const needsVersionBump = version3 === null || version3 < 2;
2215
+ const needsVersionBump = version3 === null || version3 < 3;
2210
2216
  if (defaults3.length === 0) {
2211
2217
  return needsVersionBump;
2212
2218
  }
@@ -2273,7 +2279,7 @@ function projectToSettingsPatch(project) {
2273
2279
  setupScript: project.setupScript?.trim() ? project.setupScript : null,
2274
2280
  runCommand: project.runCommand?.trim() ? project.runCommand : null,
2275
2281
  commitMethod: project.commitMethod ?? null,
2276
- disabledTools: project.disabledTools ?? [],
2282
+ disabledTools: project.disabledTools ?? getDefaultOffToolNames(),
2277
2283
  commands: project.commands ?? []
2278
2284
  };
2279
2285
  }
@@ -2335,7 +2341,7 @@ var init_project_settings = __esm({
2335
2341
  init_commit_methods();
2336
2342
  init_project_tools();
2337
2343
  init_tool_display();
2338
- PROJECT_SETTINGS_VERSION = 2;
2344
+ PROJECT_SETTINGS_VERSION = 3;
2339
2345
  PROJECT_SETTINGS_FILENAME = "tarsk-project.json";
2340
2346
  PROJECT_ICON_FILENAME = "tarsk-project-icon.png";
2341
2347
  PNG_DATA_URL_PREFIX = "data:image/png;base64,";
@@ -4234,7 +4240,7 @@ function toBufferedLogData(data) {
4234
4240
  return toJsonSafeLogData(data);
4235
4241
  }
4236
4242
  function createLogger(event) {
4237
- function log160(level, message, data) {
4243
+ function log161(level, message, data) {
4238
4244
  const willBuffer = level >= LOG_BUFFER_MIN_LEVEL && level < LogLevel.SILENT;
4239
4245
  const willEmit = level >= globalLevel;
4240
4246
  if (!willBuffer && !willEmit) {
@@ -4256,10 +4262,10 @@ function createLogger(event) {
4256
4262
  emit(entry);
4257
4263
  }
4258
4264
  return {
4259
- debug: (message, data) => log160(LogLevel.DEBUG, message, data),
4260
- info: (message, data) => log160(LogLevel.INFO, message, data),
4261
- warn: (message, data) => log160(LogLevel.WARN, message, data),
4262
- error: (message, data) => log160(LogLevel.ERROR, message, data)
4265
+ debug: (message, data) => log161(LogLevel.DEBUG, message, data),
4266
+ info: (message, data) => log161(LogLevel.INFO, message, data),
4267
+ warn: (message, data) => log161(LogLevel.WARN, message, data),
4268
+ error: (message, data) => log161(LogLevel.ERROR, message, data)
4263
4269
  };
4264
4270
  }
4265
4271
  var LogLevel, LOG_BUFFER_CAPACITY, LOG_BUFFER_MIN_LEVEL, LOG_LEVEL_NAMES, globalLevel, transports, nextBufferId, logBuffer, logBufferListeners, LEVEL_CONSOLE;
@@ -14688,7 +14694,7 @@ var init_public_tunnel = __esm({
14688
14694
  "../shared/dist/public-tunnel.js"() {
14689
14695
  "use strict";
14690
14696
  PUBLIC_TUNNEL_ORIGIN = "https://home.tarsk.io";
14691
- PUBLIC_TUNNEL_ZONE = "packageup.io";
14697
+ PUBLIC_TUNNEL_ZONE = "tarsk.net";
14692
14698
  PUBLIC_TUNNEL_CLAIM_PATH = "/_tarsk/claim";
14693
14699
  PUBLIC_TUNNEL_SECRET_HEADER = "x-tarsk-tunnel-secret";
14694
14700
  TUNNEL_ACCESS_COOKIE = "tarsk_tunnel_access";
@@ -38108,6 +38114,12 @@ var init_conversation_usage = __esm({
38108
38114
 
38109
38115
  // src/features/conversations/conversations.database.ts
38110
38116
  import { randomUUID } from "crypto";
38117
+ function historyEventProjection(options2) {
38118
+ if (options2?.omitEvents) {
38119
+ return "NULL AS response_events";
38120
+ }
38121
+ return `${RESPONSE_EVENTS_DISPLAY_SQL} AS response_events`;
38122
+ }
38111
38123
  function extractAssistantText(content) {
38112
38124
  if (typeof content === "string") {
38113
38125
  return content;
@@ -38683,8 +38695,9 @@ async function updateMessage(db, messageId, content, events, imageUrl, requestMo
38683
38695
  throw error2;
38684
38696
  }
38685
38697
  }
38686
- async function getConversationHistory(db, threadId) {
38698
+ async function getConversationHistory(db, threadId, options2) {
38687
38699
  try {
38700
+ const eventProjection = historyEventProjection(options2);
38688
38701
  const result = await db.execute(
38689
38702
  `
38690
38703
  SELECT
@@ -38692,7 +38705,7 @@ async function getConversationHistory(db, threadId) {
38692
38705
  request_message, request_model, request_attachments, request_steeringMessages,
38693
38706
  request_planMode, request_ralphMode, request_orchestrateMode,
38694
38707
  request_imageMode, request_checkpointRef, request_attribution,
38695
- response_content, ${RESPONSE_EVENTS_DISPLAY_SQL} AS response_events, response_completedAt,
38708
+ response_content, ${eventProjection}, response_completedAt,
38696
38709
  response_imageUrl, response_summary, input_tokens, output_tokens,
38697
38710
  agent_session_id, agent_session_entry_start_seq, agent_session_entry_end_seq
38698
38711
  FROM conversation_history
@@ -38733,9 +38746,10 @@ async function getDeletedConversationHistory(db, threadId) {
38733
38746
  throw error2;
38734
38747
  }
38735
38748
  }
38736
- async function getConversationHistoryByConversationId(db, threadId, conversationId, eventLimit) {
38749
+ async function getConversationHistoryByConversationId(db, threadId, conversationId, eventLimit, options2) {
38737
38750
  try {
38738
- const eventProjection = eventLimit === void 0 ? `${RESPONSE_EVENTS_DISPLAY_SQL} AS response_events` : `CASE WHEN id IN (
38751
+ const omitEvents = options2?.omitEvents === true;
38752
+ const eventProjection = omitEvents ? historyEventProjection(options2) : eventLimit === void 0 ? `${RESPONSE_EVENTS_DISPLAY_SQL} AS response_events` : `CASE WHEN id IN (
38739
38753
  SELECT recent.id
38740
38754
  FROM conversation_history recent
38741
38755
  WHERE recent.threadId = ?
@@ -38759,7 +38773,7 @@ async function getConversationHistoryByConversationId(db, threadId, conversation
38759
38773
  WHERE threadId = ? AND conversationId = ? AND status = ?
38760
38774
  ORDER BY timestamp ASC
38761
38775
  `,
38762
- eventLimit === void 0 ? [threadId, conversationId, "active"] : [
38776
+ omitEvents || eventLimit === void 0 ? [threadId, conversationId, "active"] : [
38763
38777
  threadId,
38764
38778
  conversationId,
38765
38779
  "active",
@@ -39164,24 +39178,29 @@ async function markConversationHistoryAsDeleted(db, threadId) {
39164
39178
  throw error2;
39165
39179
  }
39166
39180
  }
39167
- async function getConversationIds(db, threadId) {
39181
+ async function queryConversationIds(db, threadId, sql, failureLog) {
39168
39182
  try {
39169
- const result = await db.execute(
39170
- `
39171
- SELECT conversationId FROM conversation_history
39172
- WHERE threadId = ?
39173
- GROUP BY conversationId
39174
- ORDER BY conversationId DESC
39175
- `,
39176
- [threadId]
39177
- );
39183
+ const result = await db.execute(sql, [threadId]);
39178
39184
  const rows = result.rows;
39179
39185
  return rows.map((row) => row.conversationId);
39180
39186
  } catch (error2) {
39181
- log6.error("Failed to get conversation IDs", { error: error2, threadId });
39187
+ log6.error(failureLog, { error: error2, threadId });
39182
39188
  throw error2;
39183
39189
  }
39184
39190
  }
39191
+ async function getConversationIds(db, threadId) {
39192
+ return queryConversationIds(
39193
+ db,
39194
+ threadId,
39195
+ `
39196
+ SELECT conversationId FROM conversation_history
39197
+ WHERE threadId = ?
39198
+ GROUP BY conversationId
39199
+ ORDER BY conversationId DESC
39200
+ `,
39201
+ "Failed to get conversation IDs"
39202
+ );
39203
+ }
39185
39204
  async function getRecentConversations(db, limit3, offset = 0, projectId, excludeRead = false, order = DEFAULT_RECENT_CONVERSATIONS_ORDER) {
39186
39205
  try {
39187
39206
  const projectFilter = projectId ? "INNER JOIN threads ON threads.id = turns.threadId AND threads.projectId = ?" : "";
@@ -39279,6 +39298,7 @@ async function getRecentConversations(db, limit3, offset = 0, projectId, exclude
39279
39298
  FROM conversation_completions completions
39280
39299
  WHERE completions.threadId = recent.threadId
39281
39300
  AND completions.conversationId = recent.conversationId
39301
+ AND completions.dismissedAt IS NULL
39282
39302
  ) as isComplete
39283
39303
  FROM recent_conversations recent
39284
39304
  -- LEFT JOIN so a conversation with no turns yet still comes back, with
@@ -39382,22 +39402,114 @@ async function markConversationRead(db, threadId, conversationId) {
39382
39402
  }
39383
39403
  }
39384
39404
  async function markConversationComplete(db, threadId, conversationId) {
39385
- await db.execute(
39386
- `
39405
+ try {
39406
+ await db.execute(
39407
+ `
39387
39408
  INSERT INTO conversation_completions (threadId, conversationId, completedAt)
39388
39409
  VALUES (?, ?, ?)
39389
39410
  ON CONFLICT (threadId, conversationId)
39390
39411
  DO UPDATE SET completedAt = excluded.completedAt
39412
+ WHERE conversation_completions.dismissedAt IS NULL
39391
39413
  `,
39392
- [threadId, conversationId, (/* @__PURE__ */ new Date()).toISOString()]
39393
- );
39414
+ [threadId, conversationId, (/* @__PURE__ */ new Date()).toISOString()]
39415
+ );
39416
+ } catch (error2) {
39417
+ log6.error("Failed to mark conversation complete", { error: error2, threadId, conversationId });
39418
+ throw error2;
39419
+ }
39394
39420
  }
39395
39421
  async function unmarkConversationComplete(db, threadId, conversationId) {
39396
- await db.execute(
39397
- "DELETE FROM conversation_completions WHERE threadId = ? AND conversationId = ?",
39398
- [threadId, conversationId]
39422
+ try {
39423
+ await db.execute(
39424
+ `
39425
+ UPDATE conversation_completions
39426
+ SET dismissedAt = ?
39427
+ WHERE threadId = ?
39428
+ AND conversationId = ?
39429
+ AND dismissedAt IS NULL
39430
+ `,
39431
+ [(/* @__PURE__ */ new Date()).toISOString(), threadId, conversationId]
39432
+ );
39433
+ } catch (error2) {
39434
+ log6.error("Failed to dismiss conversation completion", { error: error2, threadId, conversationId });
39435
+ throw error2;
39436
+ }
39437
+ }
39438
+ async function clearThreadCompletionMarks(db, threadId) {
39439
+ try {
39440
+ await db.execute("DELETE FROM conversation_completions WHERE threadId = ?", [threadId]);
39441
+ } catch (error2) {
39442
+ log6.error("Failed to clear thread completion marks", { error: error2, threadId });
39443
+ throw error2;
39444
+ }
39445
+ }
39446
+ async function getCompletedConversationIds(db, threadId) {
39447
+ return queryConversationIds(
39448
+ db,
39449
+ threadId,
39450
+ `
39451
+ SELECT conversationId
39452
+ FROM conversation_completions
39453
+ WHERE threadId = ? AND dismissedAt IS NULL
39454
+ `,
39455
+ "Failed to get completed conversation IDs"
39399
39456
  );
39400
39457
  }
39458
+ async function getConversationCompletionRows(db, threadId) {
39459
+ try {
39460
+ const result = await db.execute(
39461
+ `
39462
+ SELECT conversationId, dismissedAt
39463
+ FROM conversation_completions
39464
+ WHERE threadId = ?
39465
+ `,
39466
+ [threadId]
39467
+ );
39468
+ const rows = result.rows;
39469
+ return rows.map((row) => ({
39470
+ conversationId: row.conversationId,
39471
+ dismissedAt: row.dismissedAt
39472
+ }));
39473
+ } catch (error2) {
39474
+ log6.error("Failed to get conversation completion rows", { error: error2, threadId });
39475
+ throw error2;
39476
+ }
39477
+ }
39478
+ async function getMergeableConversationIds(db, threadId) {
39479
+ try {
39480
+ const result = await db.execute(
39481
+ `
39482
+ SELECT mergeable.conversationId
39483
+ FROM (
39484
+ SELECT DISTINCT conversationId
39485
+ FROM conversation_history
39486
+ WHERE threadId = ? AND status = 'active'
39487
+
39488
+ UNION
39489
+
39490
+ SELECT threads.currentConversationId AS conversationId
39491
+ FROM threads
39492
+ WHERE threads.id = ?
39493
+ AND threads.currentConversationId IS NOT NULL
39494
+ AND NOT EXISTS (
39495
+ SELECT 1 FROM conversation_history turns
39496
+ WHERE turns.threadId = threads.id AND turns.status = 'active'
39497
+ )
39498
+ ) mergeable
39499
+ WHERE NOT EXISTS (
39500
+ SELECT 1 FROM conversation_archives archives
39501
+ WHERE archives.threadId = ? AND archives.conversationId = mergeable.conversationId
39502
+ )
39503
+ `,
39504
+ [threadId, threadId, threadId]
39505
+ );
39506
+ const rows = result.rows;
39507
+ return rows.map((row) => row.conversationId);
39508
+ } catch (error2) {
39509
+ log6.error("Failed to get mergeable conversation IDs", { error: error2, threadId });
39510
+ throw error2;
39511
+ }
39512
+ }
39401
39513
  async function unmarkConversationRead(db, threadId, conversationId) {
39402
39514
  try {
39403
39515
  await db.execute("DELETE FROM conversation_reads WHERE threadId = ? AND conversationId = ?", [
@@ -39967,11 +40079,11 @@ async function tableColumnNames(db, table) {
39967
40079
  const info = await db.execute(`PRAGMA table_info(${table})`);
39968
40080
  return new Set(info.rows.map((col) => col.name));
39969
40081
  }
39970
- function addColumn(table, column, definition, log160) {
40082
+ function addColumn(table, column, definition, log161) {
39971
40083
  return {
39972
40084
  table,
39973
40085
  column,
39974
- log: log160,
40086
+ log: log161,
39975
40087
  statements: [`ALTER TABLE ${table} ADD COLUMN ${definition}`]
39976
40088
  };
39977
40089
  }
@@ -40377,6 +40489,14 @@ async function migrateProjectTasksSchema(db) {
40377
40489
  column: "modified",
40378
40490
  log: "[db] Running migration: Adding modified column to project_tasks",
40379
40491
  statements: [`ALTER TABLE project_tasks ADD COLUMN modified INTEGER NOT NULL DEFAULT 0`]
40492
+ },
40493
+ {
40494
+ table: "project_tasks",
40495
+ column: "activeRunCount",
40496
+ log: "[db] Running migration: Adding activeRunCount column to project_tasks",
40497
+ statements: [
40498
+ `ALTER TABLE project_tasks ADD COLUMN activeRunCount INTEGER NOT NULL DEFAULT 0`
40499
+ ]
40380
40500
  }
40381
40501
  ]);
40382
40502
  await db.execute(`
@@ -40387,6 +40507,14 @@ async function migrateProjectTasksSchema(db) {
40387
40507
  CREATE INDEX IF NOT EXISTS idx_project_tasks_schedule_recurring
40388
40508
  ON project_tasks(triggerType, scheduleCron)
40389
40509
  `);
40510
+ await db.execute(`
40511
+ CREATE INDEX IF NOT EXISTS idx_project_tasks_threadId
40512
+ ON project_tasks(threadId)
40513
+ `);
40514
+ await db.execute(`
40515
+ CREATE INDEX IF NOT EXISTS idx_project_tasks_event
40516
+ ON project_tasks(projectId, triggerType, eventKey)
40517
+ `);
40390
40518
  }
40391
40519
  async function migrateProjectTagsSchema(db) {
40392
40520
  const projectTagsExists = await db.execute(
@@ -40732,14 +40860,14 @@ async function migrateMcpOAuthSchema(db) {
40732
40860
  `);
40733
40861
  }
40734
40862
  }
40735
- async function createTableIfMissing(db, table, log160, sql) {
40863
+ async function createTableIfMissing(db, table, log161, sql) {
40736
40864
  const exists = await db.execute(
40737
40865
  `SELECT name FROM sqlite_master WHERE type='table' AND name='${table}'`
40738
40866
  );
40739
40867
  if (exists.rows.length > 0) {
40740
40868
  return;
40741
40869
  }
40742
- tarskDebugLog(log160);
40870
+ tarskDebugLog(log161);
40743
40871
  await db.execute(sql);
40744
40872
  }
40745
40873
  async function migrateDailySpendSchema(db) {
@@ -41020,19 +41148,27 @@ async function migrateConversationCompletions(db) {
41020
41148
  const exists = await db.execute(
41021
41149
  `SELECT name FROM sqlite_master WHERE type='table' AND name='conversation_completions'`
41022
41150
  );
41023
- if (exists.rows.length > 0) {
41024
- return;
41025
- }
41026
- tarskDebugLog("[db] Running migration: Creating conversation_completions table");
41027
- await db.execute(`
41151
+ if (exists.rows.length === 0) {
41152
+ tarskDebugLog("[db] Running migration: Creating conversation_completions table");
41153
+ await db.execute(`
41028
41154
  CREATE TABLE conversation_completions (
41029
41155
  threadId TEXT NOT NULL,
41030
41156
  conversationId TEXT NOT NULL,
41031
41157
  completedAt TEXT NOT NULL,
41158
+ dismissedAt TEXT,
41032
41159
  PRIMARY KEY (threadId, conversationId),
41033
41160
  FOREIGN KEY (threadId) REFERENCES threads(id) ON DELETE CASCADE
41034
41161
  )
41035
41162
  `);
41163
+ }
41164
+ await ensureColumns(db, [
41165
+ addColumn(
41166
+ "conversation_completions",
41167
+ "dismissedAt",
41168
+ "dismissedAt TEXT",
41169
+ "[db] Running migration: Adding dismissedAt column to conversation_completions"
41170
+ )
41171
+ ]);
41036
41172
  }
41037
41173
  async function migrateCrossProjectLaunches(db) {
41038
41174
  const exists = await db.execute(
@@ -41310,12 +41446,14 @@ var init_database = __esm({
41310
41446
  )
41311
41447
  `,
41312
41448
  // Conversations completed after their pull request merged. A subsequent
41313
- // user message clears the marker and makes the conversation active again.
41449
+ // user message dismisses the marker (dismissedAt set) and makes the
41450
+ // conversation active again; the merged-PR scanner never resurrects it.
41314
41451
  `
41315
41452
  CREATE TABLE IF NOT EXISTS conversation_completions (
41316
41453
  threadId TEXT NOT NULL,
41317
41454
  conversationId TEXT NOT NULL,
41318
41455
  completedAt TEXT NOT NULL,
41456
+ dismissedAt TEXT,
41319
41457
  PRIMARY KEY (threadId, conversationId),
41320
41458
  FOREIGN KEY (threadId) REFERENCES threads(id) ON DELETE CASCADE
41321
41459
  )
@@ -41422,6 +41560,7 @@ var init_database = __esm({
41422
41560
  lastTriggeredAt TEXT,
41423
41561
  lastRunStatus TEXT,
41424
41562
  modified INTEGER NOT NULL DEFAULT 0,
41563
+ activeRunCount INTEGER NOT NULL DEFAULT 0,
41425
41564
  createdAt TEXT NOT NULL,
41426
41565
  updatedAt TEXT NOT NULL,
41427
41566
  FOREIGN KEY (projectId) REFERENCES projects(id) ON DELETE CASCADE
@@ -41443,6 +41582,16 @@ var init_database = __esm({
41443
41582
  `,
41444
41583
  `
41445
41584
  CREATE INDEX IF NOT EXISTS idx_project_task_runs_task ON project_task_runs(taskId, createdAt DESC)
41585
+ `,
41586
+ // In-flight automation threads. A task can have several at once; working
41587
+ // stays set until the last one finishes. threadId on project_tasks is only
41588
+ // the latest launch, so completion is matched through this table.
41589
+ `
41590
+ CREATE TABLE IF NOT EXISTS project_task_active_runs (
41591
+ threadId TEXT PRIMARY KEY,
41592
+ taskId TEXT NOT NULL,
41593
+ FOREIGN KEY (taskId) REFERENCES project_tasks(id) ON DELETE CASCADE
41594
+ )
41446
41595
  `,
41447
41596
  // Create project task tag tables (ADR 0004)
41448
41597
  `
@@ -41586,6 +41735,15 @@ var init_database = __esm({
41586
41735
  `,
41587
41736
  `
41588
41737
  CREATE INDEX IF NOT EXISTS idx_project_tasks_projectId ON project_tasks(projectId)
41738
+ `,
41739
+ // threadId lookups run on every chat message (getTaskByThreadId) and at
41740
+ // task-run completion (clearWorkingByThreadId): keep them off full scans.
41741
+ `
41742
+ CREATE INDEX IF NOT EXISTS idx_project_tasks_threadId ON project_tasks(threadId)
41743
+ `,
41744
+ // Event-trigger dispatch (getEnabledEventTasksByKey) filters on all three.
41745
+ `
41746
+ CREATE INDEX IF NOT EXISTS idx_project_tasks_event ON project_tasks(projectId, triggerType, eventKey)
41589
41747
  `,
41590
41748
  `
41591
41749
  CREATE INDEX IF NOT EXISTS idx_project_tags_projectId ON project_tags(projectId)
@@ -54324,6 +54482,9 @@ function extractEditMissAnchorTokens(oldText) {
54324
54482
  for (const match3 of oldText.matchAll(/@[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+/g)) {
54325
54483
  add(match3[0]);
54326
54484
  }
54485
+ for (const match3 of oldText.matchAll(/0x[0-9a-fA-F]{3,}/g)) {
54486
+ add(match3[0]);
54487
+ }
54327
54488
  tokens.sort(function(left2, right2) {
54328
54489
  return right2.length - left2.length;
54329
54490
  });
@@ -54369,6 +54530,180 @@ function isWeakClosestLine(content, lineIndex) {
54369
54530
  const line = normalizeToLF(content).split("\n")[lineIndex]?.trim() ?? "";
54370
54531
  return line.length <= 2;
54371
54532
  }
54533
+ function isCommonEditMissToken(token) {
54534
+ return EDIT_MISS_COMMON_TOKENS.has(token) || EDIT_MISS_COMMON_TOKENS.has(token.toLowerCase());
54535
+ }
54536
+ function isSpecificIdentifier(token) {
54537
+ if (token.length < 4 || isCommonEditMissToken(token)) {
54538
+ return false;
54539
+ }
54540
+ if (/[a-z][A-Z]/.test(token)) {
54541
+ return true;
54542
+ }
54543
+ return token.length >= 8;
54544
+ }
54545
+ function extractSpecificTokens(text) {
54546
+ const tokens = [];
54547
+ const seen = /* @__PURE__ */ new Set();
54548
+ function add(token) {
54549
+ if (token.length < 4 || isCommonEditMissToken(token) || seen.has(token)) {
54550
+ return;
54551
+ }
54552
+ if (!isSpecificIdentifier(token) && !token.startsWith("@") && !token.startsWith("0x") && !/[\s./-]/.test(token)) {
54553
+ return;
54554
+ }
54555
+ seen.add(token);
54556
+ tokens.push(token);
54557
+ }
54558
+ for (const match3 of text.matchAll(/["']([^"']{4,})["']/g)) {
54559
+ add(match3[1] ?? "");
54560
+ }
54561
+ for (const match3 of text.matchAll(/@[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+/g)) {
54562
+ add(match3[0]);
54563
+ }
54564
+ for (const match3 of text.matchAll(/0x[0-9a-fA-F]{3,}/g)) {
54565
+ add(match3[0]);
54566
+ }
54567
+ const outsideQuotes = text.replace(/["'][^"']*["']/g, " ");
54568
+ for (const match3 of outsideQuotes.matchAll(/[A-Za-z_$][\w$]*/g)) {
54569
+ add(match3[0]);
54570
+ }
54571
+ tokens.sort(function(left2, right2) {
54572
+ return right2.length - left2.length;
54573
+ });
54574
+ return tokens;
54575
+ }
54576
+ function sharesSubstantialPrefix(oldLine, fileLine) {
54577
+ const oldNorm = normalizeForFuzzyMatch(oldLine).trim();
54578
+ const fileNorm = normalizeForFuzzyMatch(fileLine).trim();
54579
+ if (oldNorm.length === 0 || fileNorm.length === 0) {
54580
+ return false;
54581
+ }
54582
+ const prefixLength = commonPrefixLength(oldNorm, fileNorm);
54583
+ const shorter = Math.min(oldNorm.length, fileNorm.length);
54584
+ if (prefixLength === shorter) {
54585
+ return true;
54586
+ }
54587
+ return prefixLength >= 12 && shorter - prefixLength <= 8;
54588
+ }
54589
+ function lineKeepsDistinctiveTokens(oldLine, fileLine) {
54590
+ const tokens = extractSpecificTokens(oldLine);
54591
+ if (tokens.length === 0) {
54592
+ return sharesSubstantialPrefix(oldLine, fileLine);
54593
+ }
54594
+ for (const token of tokens) {
54595
+ if (!fileLine.includes(token)) {
54596
+ return false;
54597
+ }
54598
+ }
54599
+ return true;
54600
+ }
54601
+ function commonSuffixLength(left2, right2) {
54602
+ const limit3 = Math.min(left2.length, right2.length);
54603
+ let length = 0;
54604
+ for (let index4 = 0; index4 < limit3; index4++) {
54605
+ if (left2[left2.length - 1 - index4] !== right2[right2.length - 1 - index4]) {
54606
+ break;
54607
+ }
54608
+ length++;
54609
+ }
54610
+ return length;
54611
+ }
54612
+ function identifiersAreSimilar(left2, right2) {
54613
+ if (left2.toLowerCase() === right2.toLowerCase()) {
54614
+ return true;
54615
+ }
54616
+ if (commonPrefixLength(left2, right2) >= 6) {
54617
+ return true;
54618
+ }
54619
+ return commonSuffixLength(left2, right2) >= 8;
54620
+ }
54621
+ function extractCallHeads(oldText) {
54622
+ const heads = [];
54623
+ const seen = /* @__PURE__ */ new Set();
54624
+ for (const match3 of oldText.matchAll(/([A-Za-z_$][\w$]*)\s*\(/g)) {
54625
+ const head = match3[1] ?? "";
54626
+ if (head.length < 4 || isCommonEditMissToken(head) || seen.has(head)) {
54627
+ continue;
54628
+ }
54629
+ seen.add(head);
54630
+ heads.push(head);
54631
+ }
54632
+ heads.sort(function(left2, right2) {
54633
+ return right2.length - left2.length;
54634
+ });
54635
+ return heads;
54636
+ }
54637
+ function trimSimilarLine(line) {
54638
+ const trimmed = line.trimEnd();
54639
+ if (trimmed.length <= 160) {
54640
+ return trimmed;
54641
+ }
54642
+ return `${trimmed.slice(0, 157)}...`;
54643
+ }
54644
+ function findSimilarCallLines(content, oldText) {
54645
+ const heads = extractCallHeads(oldText);
54646
+ const specificIdentifiers = extractSpecificTokens(oldText).filter(function(token) {
54647
+ return /^[A-Za-z_$]/.test(token);
54648
+ });
54649
+ const primary = heads[0] ?? specificIdentifiers[0];
54650
+ if (!primary) {
54651
+ return [];
54652
+ }
54653
+ const fileLines = normalizeToLF(content).split("\n");
54654
+ const exact = [];
54655
+ const similar = [];
54656
+ const primaryLower = primary.toLowerCase();
54657
+ for (let lineIndex = 0; lineIndex < fileLines.length; lineIndex++) {
54658
+ const line = fileLines[lineIndex] ?? "";
54659
+ if (line.trim().length < 4) {
54660
+ continue;
54661
+ }
54662
+ if (line.toLowerCase().includes(primaryLower)) {
54663
+ exact.push({ lineNumber: lineIndex + 1, text: trimSimilarLine(line) });
54664
+ if (exact.length >= EDIT_MISS_SIMILAR_LINE_LIMIT) {
54665
+ break;
54666
+ }
54667
+ continue;
54668
+ }
54669
+ if (exact.length > 0) {
54670
+ continue;
54671
+ }
54672
+ const identifiers = line.match(/[A-Za-z_$][\w$]{3,}/g) ?? [];
54673
+ const matched = identifiers.some(function(identifier) {
54674
+ return identifiersAreSimilar(identifier, primary);
54675
+ });
54676
+ if (matched && similar.length < EDIT_MISS_SIMILAR_LINE_LIMIT) {
54677
+ similar.push({ lineNumber: lineIndex + 1, text: trimSimilarLine(line) });
54678
+ }
54679
+ }
54680
+ return exact.length > 0 ? exact : similar;
54681
+ }
54682
+ function formatAbsentTokenHint(content, oldText) {
54683
+ const intro = "No line in the file matches the start of oldText. oldText may be stale, minified, or use different quoting/spacing than the file \u2014 correct it from a unique nearby substring (or editMany retryHints/currentSnippet); do not invent compact JSON.";
54684
+ const missing = extractSpecificTokens(oldText).filter(function(token) {
54685
+ return !content.includes(token);
54686
+ }).slice(0, 6);
54687
+ const similar = findSimilarCallLines(content, oldText);
54688
+ const parts = [intro];
54689
+ if (missing.length > 0) {
54690
+ const listed = missing.map(function(token) {
54691
+ return JSON.stringify(token);
54692
+ }).join(", ");
54693
+ parts.push(`Not in this file: ${listed}.`);
54694
+ }
54695
+ if (similar.length > 0) {
54696
+ const lines = similar.map(function(entry) {
54697
+ return ` ${entry.lineNumber}| ${entry.text}`;
54698
+ }).join("\n");
54699
+ parts.push(
54700
+ "Similar lines (read the file and copy the real text; these lines are not oldText):\n" + lines
54701
+ );
54702
+ } else {
54703
+ parts.push("Read the file and copy the real text. Do not retry this oldText.");
54704
+ }
54705
+ return parts.join("\n");
54706
+ }
54372
54707
  function describeFormattingDrift(oldText, fileLine) {
54373
54708
  const compactFile = collapseWhitespace(fileLine);
54374
54709
  const oldLines = normalizeToLF(oldText).split("\n").map(function(line) {
@@ -54484,14 +54819,26 @@ ${slice.join("\n")}
54484
54819
  function formatEditMissHint(content, oldText) {
54485
54820
  const closestLine = findClosestLineForOldText(content, oldText);
54486
54821
  const tokenAnchor = findTokenAnchorLine(content, oldText);
54822
+ const fileLinesForGate = normalizeToLF(content).split("\n");
54823
+ const firstOldLine = splitLines2(normalizeToLF(oldText)).find(function(line) {
54824
+ return line.trim().length > 0;
54825
+ }) ?? "";
54487
54826
  let lineIndex = closestLine;
54488
54827
  let viaToken;
54489
- if (tokenAnchor && (closestLine === void 0 || isWeakClosestLine(content, closestLine))) {
54490
- lineIndex = tokenAnchor.lineIndex;
54491
- viaToken = tokenAnchor.token;
54828
+ const closestUsable = closestLine !== void 0 && !isWeakClosestLine(content, closestLine) && lineKeepsDistinctiveTokens(firstOldLine, fileLinesForGate[closestLine] ?? "");
54829
+ if (!closestUsable && tokenAnchor) {
54830
+ const anchorLine = fileLinesForGate[tokenAnchor.lineIndex] ?? "";
54831
+ if (!isWeakClosestLine(content, tokenAnchor.lineIndex) && anchorLine.includes(tokenAnchor.token)) {
54832
+ lineIndex = tokenAnchor.lineIndex;
54833
+ viaToken = tokenAnchor.token;
54834
+ } else {
54835
+ lineIndex = void 0;
54836
+ }
54837
+ } else if (!closestUsable) {
54838
+ lineIndex = void 0;
54492
54839
  }
54493
54840
  if (lineIndex === void 0 || isWeakClosestLine(content, lineIndex) && viaToken === void 0) {
54494
- return "No line in the file matches the start of oldText. oldText may be stale, minified, or use different quoting/spacing than the file \u2014 correct it from a unique nearby substring (or editMany retryHints/currentSnippet); do not invent compact JSON and do not start a new read turn.";
54841
+ return formatAbsentTokenHint(content, oldText);
54495
54842
  }
54496
54843
  const fileLines = normalizeToLF(content).split("\n");
54497
54844
  const oldLines = splitLines2(normalizeToLF(oldText));
@@ -54631,7 +54978,7 @@ function mergeLineRanges(ranges) {
54631
54978
  }
54632
54979
  return merged;
54633
54980
  }
54634
- var EDIT_MISS_COMMON_TOKENS, EDIT_MISS_HINT_MAX_LINES, EDIT_MISS_HINT_PREFIX_LINES;
54981
+ var EDIT_MISS_COMMON_TOKENS, EDIT_MISS_SIMILAR_LINE_LIMIT, EDIT_MISS_HINT_MAX_LINES, EDIT_MISS_HINT_PREFIX_LINES;
54635
54982
  var init_edit_diff = __esm({
54636
54983
  "src/tools/edit-diff.ts"() {
54637
54984
  "use strict";
@@ -54652,8 +54999,33 @@ var init_edit_diff = __esm({
54652
54999
  "return",
54653
55000
  "true",
54654
55001
  "false",
54655
- "null"
55002
+ "null",
55003
+ "new",
55004
+ "this",
55005
+ "let",
55006
+ "var",
55007
+ "from",
55008
+ "class",
55009
+ "string",
55010
+ "boolean",
55011
+ "number",
55012
+ "void",
55013
+ "public",
55014
+ "static",
55015
+ "async",
55016
+ "await",
55017
+ "undefined",
55018
+ "object",
55019
+ "default",
55020
+ "color",
55021
+ "side",
55022
+ "roughness",
55023
+ "opacity",
55024
+ "transparent",
55025
+ "metalness",
55026
+ "emissive"
54656
55027
  ]);
55028
+ EDIT_MISS_SIMILAR_LINE_LIMIT = 4;
54657
55029
  EDIT_MISS_HINT_MAX_LINES = 40;
54658
55030
  EDIT_MISS_HINT_PREFIX_LINES = 2;
54659
55031
  }
@@ -92919,12 +93291,12 @@ var init_poller = __esm({
92919
93291
  throw new AnthropicError("Cannot iterate over a consumed WorkPoller");
92920
93292
  }
92921
93293
  __classPrivateFieldSet2(this, _WorkPoller_consumed, true, "f");
92922
- const log160 = loggerFor2(this.client);
92923
- log160.info("poller starting", {
93294
+ const log161 = loggerFor2(this.client);
93295
+ log161.info("poller starting", {
92924
93296
  component: "work-poller",
92925
93297
  environment_id: this.environmentId
92926
93298
  });
92927
- const idle = new IdleLog(log160, this.environmentId);
93299
+ const idle = new IdleLog(log161, this.environmentId);
92928
93300
  try {
92929
93301
  let attempt = 0;
92930
93302
  while (!__classPrivateFieldGet3(this, _WorkPoller_controller, "f").signal.aborted) {
@@ -92939,11 +93311,11 @@ var init_poller = __esm({
92939
93311
  if (__classPrivateFieldGet3(this, _WorkPoller_controller, "f").signal.aborted)
92940
93312
  return;
92941
93313
  if (isFatal4xx(e2)) {
92942
- log160.error("poll failed permanently, stopping poller", { error: String(e2) });
93314
+ log161.error("poll failed permanently, stopping poller", { error: String(e2) });
92943
93315
  throw e2;
92944
93316
  }
92945
93317
  const wait = applyJitter(backoff2(attempt));
92946
- log160.warn("poll failed, backing off", { error: String(e2), backoff_ms: wait });
93318
+ log161.warn("poll failed, backing off", { error: String(e2), backoff_ms: wait });
92947
93319
  attempt++;
92948
93320
  await sleep2(wait, __classPrivateFieldGet3(this, _WorkPoller_controller, "f").signal);
92949
93321
  continue;
@@ -92957,7 +93329,7 @@ var init_poller = __esm({
92957
93329
  continue;
92958
93330
  }
92959
93331
  idle.onClaim();
92960
- log160.info("claimed work", {
93332
+ log161.info("claimed work", {
92961
93333
  component: "work-poller",
92962
93334
  environment_id: this.environmentId,
92963
93335
  work_id: work.id,
@@ -92966,7 +93338,7 @@ var init_poller = __esm({
92966
93338
  try {
92967
93339
  await __classPrivateFieldGet3(this, _WorkPoller_runnerClient, "f").beta.environments.work.ack(work.id, { environment_id: work.environment_id }, { headers: buildHeaders2([__classPrivateFieldGet3(this, _WorkPoller_requestOpts, "f")?.headers]), signal: __classPrivateFieldGet3(this, _WorkPoller_controller, "f").signal });
92968
93340
  } catch (e2) {
92969
- log160.error("ack failed", { work_id: work.id, error: String(e2) });
93341
+ log161.error("ack failed", { work_id: work.id, error: String(e2) });
92970
93342
  continue;
92971
93343
  }
92972
93344
  try {
@@ -92977,7 +93349,7 @@ var init_poller = __esm({
92977
93349
  await __classPrivateFieldGet3(this, _WorkPoller_runnerClient, "f").beta.environments.work.stop(work.id, { environment_id: work.environment_id }, { headers: buildHeaders2([__classPrivateFieldGet3(this, _WorkPoller_requestOpts, "f")?.headers]) });
92978
93350
  } catch (e2) {
92979
93351
  if (!isStatus(e2, 409))
92980
- log160.warn("stop failed", { work_id: work.id, error: String(e2) });
93352
+ log161.warn("stop failed", { work_id: work.id, error: String(e2) });
92981
93353
  }
92982
93354
  }
92983
93355
  }
@@ -92988,12 +93360,12 @@ var init_poller = __esm({
92988
93360
  }
92989
93361
  };
92990
93362
  IdleLog = class {
92991
- constructor(log160, environmentId) {
93363
+ constructor(log161, environmentId) {
92992
93364
  _IdleLog_log.set(this, void 0);
92993
93365
  _IdleLog_environmentId.set(this, void 0);
92994
93366
  _IdleLog_idleSince.set(this, void 0);
92995
93367
  _IdleLog_lastReport.set(this, 0);
92996
- __classPrivateFieldSet2(this, _IdleLog_log, log160, "f");
93368
+ __classPrivateFieldSet2(this, _IdleLog_log, log161, "f");
92997
93369
  __classPrivateFieldSet2(this, _IdleLog_environmentId, environmentId, "f");
92998
93370
  }
92999
93371
  onEmptyPoll() {
@@ -93849,13 +94221,13 @@ async function setupSkills(ctx) {
93849
94221
  if (!client)
93850
94222
  return async () => {
93851
94223
  };
93852
- const log160 = loggerFor2(client);
94224
+ const log161 = loggerFor2(client);
93853
94225
  let session = ctx.session;
93854
94226
  if (!session) {
93855
94227
  if (sessionId === void 0)
93856
94228
  return async () => {
93857
94229
  };
93858
- log160.warn("AgentToolContext.sessionId is deprecated and costs an extra session fetch; fetch the session once and set `session` instead", { component: "agent-tool-context" });
94230
+ log161.warn("AgentToolContext.sessionId is deprecated and costs an extra session fetch; fetch the session once and set `session` instead", { component: "agent-tool-context" });
93859
94231
  session = await client.beta.sessions.retrieve(sessionId);
93860
94232
  }
93861
94233
  const skillsRoot = path6.resolve(ctx.workdir, "skills");
@@ -93868,7 +94240,7 @@ async function setupSkills(ctx) {
93868
94240
  dirname27 = skill.skill_id;
93869
94241
  const dest = path6.resolve(skillsRoot, dirname27);
93870
94242
  if (dest !== skillsRoot && !dest.startsWith(skillsRoot + path6.sep)) {
93871
- log160.warn("skill name escapes the skills dir; skipping", {
94243
+ log161.warn("skill name escapes the skills dir; skipping", {
93872
94244
  component: "agent-tool-context",
93873
94245
  name: version3.name
93874
94246
  });
@@ -93879,14 +94251,14 @@ async function setupSkills(ctx) {
93879
94251
  await fs10.mkdir(dest, { recursive: true, mode: DIR_CREATE_MODE });
93880
94252
  created.push(dest);
93881
94253
  await extractSkillArchive(resp, dest);
93882
- log160.info("downloaded skill", {
94254
+ log161.info("downloaded skill", {
93883
94255
  component: "agent-tool-context",
93884
94256
  skill_id: skill.skill_id,
93885
94257
  version: version3.id,
93886
94258
  dest
93887
94259
  });
93888
94260
  } catch (e2) {
93889
- log160.warn("failed to download skill", {
94261
+ log161.warn("failed to download skill", {
93890
94262
  component: "agent-tool-context",
93891
94263
  skill_id: skill.skill_id,
93892
94264
  error: String(e2)
@@ -93896,7 +94268,7 @@ async function setupSkills(ctx) {
93896
94268
  return async () => {
93897
94269
  for (const dest of created) {
93898
94270
  await fs10.rm(dest, { recursive: true, force: true }).catch((e2) => {
93899
- log160.warn("failed to clean up skill", { component: "agent-tool-context", dest, error: String(e2) });
94271
+ log161.warn("failed to clean up skill", { component: "agent-tool-context", dest, error: String(e2) });
93900
94272
  });
93901
94273
  }
93902
94274
  };
@@ -95942,7 +96314,7 @@ async function withTimeout(p, ms2) {
95942
96314
  clearTimeout(timer2);
95943
96315
  }
95944
96316
  }
95945
- async function forceStop(client, work, log160, requestOptions) {
96317
+ async function forceStop(client, work, log161, requestOptions) {
95946
96318
  try {
95947
96319
  await client.beta.environments.work.stop(
95948
96320
  work.id,
@@ -95954,7 +96326,7 @@ async function forceStop(client, work, log160, requestOptions) {
95954
96326
  );
95955
96327
  } catch (e2) {
95956
96328
  if (!isStatus(e2, 409)) {
95957
- log160.error("force-stop on exit failed", { work_id: work.id, error: String(e2) });
96329
+ log161.error("force-stop on exit failed", { work_id: work.id, error: String(e2) });
95958
96330
  }
95959
96331
  }
95960
96332
  }
@@ -96179,10 +96551,10 @@ var init_worker = __esm({
96179
96551
  * `environmentKey` unchanged.
96180
96552
  */
96181
96553
  async function _EnvironmentWorker_handleItem2(work, environmentKey, externalSignal) {
96182
- const log160 = loggerFor2(this.client);
96554
+ const log161 = loggerFor2(this.client);
96183
96555
  const sessionsToken = sessionsTokenFromSecret(work.secret);
96184
96556
  if (work.secret && sessionsToken === null) {
96185
- log160.warn("work item carried a secret payload but no sessions token could be extracted; falling back to the environment key", { work_id: work.id });
96557
+ log161.warn("work item carried a secret payload but no sessions token could be extracted; falling back to the environment key", { work_id: work.id });
96186
96558
  }
96187
96559
  const itemCredential = sessionsToken ?? environmentKey;
96188
96560
  const sessionClient = copyClientForHelper(this.client, {
@@ -96196,12 +96568,12 @@ var init_worker = __esm({
96196
96568
  const agentToolset = await Promise.resolve().then(() => (init_node6(), node_exports2));
96197
96569
  let leaseTtlMs;
96198
96570
  let runner;
96199
- const heartbeatPromise = heartbeatLoop(sessionClient, work, lease, log160, this.requestOptions, (ttlMs) => {
96571
+ const heartbeatPromise = heartbeatLoop(sessionClient, work, lease, log161, this.requestOptions, (ttlMs) => {
96200
96572
  leaseTtlMs = ttlMs;
96201
96573
  runner?._setSendRetryWindow(ttlMs);
96202
96574
  }).catch((e2) => {
96203
96575
  if (!ctrl.signal.aborted)
96204
- log160.error("heartbeat loop failed", { work_id: work.id, error: String(e2) });
96576
+ log161.error("heartbeat loop failed", { work_id: work.id, error: String(e2) });
96205
96577
  ctrl.abort();
96206
96578
  });
96207
96579
  let cleanupSkills = async () => {
@@ -96210,7 +96582,7 @@ var init_worker = __esm({
96210
96582
  let cleanEnd = false;
96211
96583
  try {
96212
96584
  if (work.data.type !== "session") {
96213
- log160.debug("skipping non-session work item", { work_id: work.id, type: work.data.type });
96585
+ log161.debug("skipping non-session work item", { work_id: work.id, type: work.data.type });
96214
96586
  return;
96215
96587
  }
96216
96588
  const session = await sessionClient.beta.sessions.retrieve(sessionId);
@@ -96229,7 +96601,7 @@ var init_worker = __esm({
96229
96601
  try {
96230
96602
  cleanupSkills = await agentToolset.setupSkills(ctx);
96231
96603
  } catch (e2) {
96232
- log160.warn("skill setup failed", { session_id: sessionId, work_id: work.id, error: String(e2) });
96604
+ log161.warn("skill setup failed", { session_id: sessionId, work_id: work.id, error: String(e2) });
96233
96605
  }
96234
96606
  if (sessionsToken !== null && this.memorySyncIntervalMs !== null) {
96235
96607
  stores = new agentToolset.SessionMemoryStores(sessionClient, {
@@ -96241,7 +96613,7 @@ var init_worker = __esm({
96241
96613
  ctx.allowedRoots = stores.roots;
96242
96614
  ctx.readOnlyRoots = stores.readOnlyRoots;
96243
96615
  } else {
96244
- log160.debug("memory stores disabled for this item", { work_id: work.id });
96616
+ log161.debug("memory stores disabled for this item", { work_id: work.id });
96245
96617
  }
96246
96618
  const tools = typeof this.tools === "function" ? this.tools(ctx) : this.tools ?? agentToolset.betaAgentToolset20260401(ctx);
96247
96619
  runner = new SessionToolRunner(sessionId, {
@@ -96261,7 +96633,7 @@ var init_worker = __esm({
96261
96633
  } finally {
96262
96634
  try {
96263
96635
  await cleanupSkills().catch((e2) => {
96264
- log160.warn("skill cleanup failed", { session_id: sessionId, work_id: work.id, error: String(e2) });
96636
+ log161.warn("skill cleanup failed", { session_id: sessionId, work_id: work.id, error: String(e2) });
96265
96637
  });
96266
96638
  } finally {
96267
96639
  if (stores) {
@@ -96269,17 +96641,17 @@ var init_worker = __esm({
96269
96641
  if (cleanEnd) {
96270
96642
  const finishCutOff = await withTimeout(stores.finish(), boundMs);
96271
96643
  if (finishCutOff) {
96272
- log160.warn(`final memory sync cut off after ${boundMs}ms; the flush that follows still uploads changed files`, { session_id: sessionId, work_id: work.id });
96644
+ log161.warn(`final memory sync cut off after ${boundMs}ms; the flush that follows still uploads changed files`, { session_id: sessionId, work_id: work.id });
96273
96645
  }
96274
96646
  }
96275
96647
  const flushBound = new AbortController();
96276
96648
  const flushCutOff = await withTimeout(stores.flushWrites(flushBound.signal), boundMs);
96277
96649
  if (flushCutOff) {
96278
96650
  flushBound.abort();
96279
- log160.warn(`memory flush cut off after ${boundMs}ms; changed files it had not uploaded yet are not saved`, { session_id: sessionId, work_id: work.id });
96651
+ log161.warn(`memory flush cut off after ${boundMs}ms; changed files it had not uploaded yet are not saved`, { session_id: sessionId, work_id: work.id });
96280
96652
  }
96281
96653
  await stores.dispose().catch((e2) => {
96282
- log160.warn("memory store cleanup failed", {
96654
+ log161.warn("memory store cleanup failed", {
96283
96655
  session_id: sessionId,
96284
96656
  work_id: work.id,
96285
96657
  error: String(e2)
@@ -96291,9 +96663,9 @@ var init_worker = __esm({
96291
96663
  detachExternal();
96292
96664
  await heartbeatPromise;
96293
96665
  if (lease.lost) {
96294
- log160.info("lease lost; released without stopping it", { session_id: sessionId, work_id: work.id });
96666
+ log161.info("lease lost; released without stopping it", { session_id: sessionId, work_id: work.id });
96295
96667
  } else {
96296
- await forceStop(sessionClient, work, log160, this.requestOptions);
96668
+ await forceStop(sessionClient, work, log161, this.requestOptions);
96297
96669
  }
96298
96670
  }
96299
96671
  };
@@ -108830,7 +109202,7 @@ var require_node = __commonJS({
108830
109202
  var tty = __require("tty");
108831
109203
  var util3 = __require("util");
108832
109204
  exports2.init = init;
108833
- exports2.log = log160;
109205
+ exports2.log = log161;
108834
109206
  exports2.formatArgs = formatArgs;
108835
109207
  exports2.save = save;
108836
109208
  exports2.load = load;
@@ -108965,7 +109337,7 @@ var require_node = __commonJS({
108965
109337
  }
108966
109338
  return (/* @__PURE__ */ new Date()).toISOString() + " ";
108967
109339
  }
108968
- function log160(...args2) {
109340
+ function log161(...args2) {
108969
109341
  return process.stderr.write(util3.formatWithOptions(exports2.inspectOpts, ...args2) + "\n");
108970
109342
  }
108971
109343
  function save(namespaces) {
@@ -118404,7 +118776,7 @@ var require_logging_utils = __commonJS({
118404
118776
  exports2.getDebugBackend = getDebugBackend;
118405
118777
  exports2.getStructuredBackend = getStructuredBackend;
118406
118778
  exports2.setBackend = setBackend;
118407
- exports2.log = log160;
118779
+ exports2.log = log161;
118408
118780
  var events_1 = __require("events");
118409
118781
  var process12 = __importStar2(__require("process"));
118410
118782
  var util3 = __importStar2(__require("util"));
@@ -118436,7 +118808,7 @@ var require_logging_utils = __commonJS({
118436
118808
  this.func.info = (...args2) => this.invokeSeverity(LogSeverity.INFO, ...args2);
118437
118809
  this.func.warn = (...args2) => this.invokeSeverity(LogSeverity.WARNING, ...args2);
118438
118810
  this.func.error = (...args2) => this.invokeSeverity(LogSeverity.ERROR, ...args2);
118439
- this.func.sublog = (namespace2) => log160(namespace2, this.func);
118811
+ this.func.sublog = (namespace2) => log161(namespace2, this.func);
118440
118812
  }
118441
118813
  invoke(fields, ...args2) {
118442
118814
  if (this.upstream) {
@@ -118603,7 +118975,7 @@ var require_logging_utils = __commonJS({
118603
118975
  cachedBackend = backend;
118604
118976
  loggerCache.clear();
118605
118977
  }
118606
- function log160(namespace, parent) {
118978
+ function log161(namespace, parent) {
118607
118979
  if (!cachedBackend) {
118608
118980
  const enablesFlag = process12.env[exports2.env.nodeEnables];
118609
118981
  if (!enablesFlag) {
@@ -118736,7 +119108,7 @@ var require_src4 = __commonJS({
118736
119108
  exports2.HEADER_NAME = "Metadata-Flavor";
118737
119109
  exports2.HEADER_VALUE = "Google";
118738
119110
  exports2.HEADERS = Object.freeze({ [exports2.HEADER_NAME]: exports2.HEADER_VALUE });
118739
- var log160 = logger.log("gcp-metadata");
119111
+ var log161 = logger.log("gcp-metadata");
118740
119112
  exports2.METADATA_SERVER_DETECTION = Object.freeze({
118741
119113
  "assume-present": "don't try to ping the metadata server, but assume it's present",
118742
119114
  none: "don't try to ping the metadata server, but don't try to use it either",
@@ -118799,9 +119171,9 @@ var require_src4 = __commonJS({
118799
119171
  responseType: "text",
118800
119172
  timeout: requestTimeout()
118801
119173
  };
118802
- log160.info("instance request %j", req);
119174
+ log161.info("instance request %j", req);
118803
119175
  const res = await requestMethod(req);
118804
- log160.info("instance metadata is %s", res.data);
119176
+ log161.info("instance metadata is %s", res.data);
118805
119177
  const metadataFlavor = res.headers.get(exports2.HEADER_NAME);
118806
119178
  if (metadataFlavor !== exports2.HEADER_VALUE) {
118807
119179
  throw new RangeError(`Invalid response from metadata service: incorrect ${exports2.HEADER_NAME} header. Expected '${exports2.HEADER_VALUE}', got ${metadataFlavor ? `'${metadataFlavor}'` : "no header"}`);
@@ -156581,7 +156953,7 @@ var init_package = __esm({
156581
156953
  "package.json"() {
156582
156954
  package_default = {
156583
156955
  name: "tarsk",
156584
- version: "0.5.82",
156956
+ version: "0.5.83",
156585
156957
  description: "CLI for Tarsk - Project Threads Manager",
156586
156958
  keywords: [
156587
156959
  "cli",
@@ -158714,17 +159086,64 @@ async function getTaskByThreadId(db, threadId) {
158714
159086
  });
158715
159087
  return result.rows[0] ?? null;
158716
159088
  }
159089
+ async function decrementActiveRunCount(db, taskId) {
159090
+ await db.execute({
159091
+ sql: `UPDATE project_tasks
159092
+ SET activeRunCount = MAX(activeRunCount - 1, 0),
159093
+ working = CASE WHEN activeRunCount - 1 > 0 THEN 1 ELSE 0 END,
159094
+ updatedAt = ?
159095
+ WHERE id = ? AND activeRunCount > 0`,
159096
+ args: [(/* @__PURE__ */ new Date()).toISOString(), taskId]
159097
+ });
159098
+ }
159099
+ async function registerActiveTaskRun(db, taskId, threadId) {
159100
+ await db.execute({
159101
+ sql: `INSERT INTO project_task_active_runs (threadId, taskId) VALUES (?, ?)`,
159102
+ args: [threadId, taskId]
159103
+ });
159104
+ }
159105
+ async function releaseAutomatedRun(db, taskId, threadId) {
159106
+ if (threadId) {
159107
+ const active = await db.execute({
159108
+ sql: `SELECT taskId FROM project_task_active_runs WHERE threadId = ?`,
159109
+ args: [threadId]
159110
+ });
159111
+ if (active.rows.length > 0) {
159112
+ await clearWorkingByThreadId(db, threadId);
159113
+ return;
159114
+ }
159115
+ }
159116
+ await decrementActiveRunCount(db, taskId);
159117
+ }
158717
159118
  async function clearWorkingByThreadId(db, threadId) {
159119
+ const active = await db.execute({
159120
+ sql: `SELECT taskId FROM project_task_active_runs WHERE threadId = ?`,
159121
+ args: [threadId]
159122
+ });
159123
+ const taskId = active.rows[0] ? String(active.rows[0].taskId) : null;
159124
+ if (taskId) {
159125
+ await db.execute({
159126
+ sql: `DELETE FROM project_task_active_runs WHERE threadId = ?`,
159127
+ args: [threadId]
159128
+ });
159129
+ await decrementActiveRunCount(db, taskId);
159130
+ return;
159131
+ }
158718
159132
  await db.execute({
158719
- sql: `UPDATE project_tasks SET working = 0, updatedAt = ? WHERE threadId = ?`,
159133
+ sql: `UPDATE project_tasks
159134
+ SET working = 0, updatedAt = ?
159135
+ WHERE threadId = ? AND activeRunCount = 0`,
158720
159136
  args: [(/* @__PURE__ */ new Date()).toISOString(), threadId]
158721
159137
  });
158722
159138
  }
158723
159139
  async function clearAllWorkingFlags(db) {
158724
159140
  const result = await db.execute({
158725
- sql: `UPDATE project_tasks SET working = 0, updatedAt = ? WHERE working = 1`,
159141
+ sql: `UPDATE project_tasks
159142
+ SET working = 0, activeRunCount = 0, updatedAt = ?
159143
+ WHERE working = 1 OR activeRunCount > 0`,
158726
159144
  args: [(/* @__PURE__ */ new Date()).toISOString()]
158727
159145
  });
159146
+ await db.execute(`DELETE FROM project_task_active_runs`);
158728
159147
  return result.rowsAffected ?? 0;
158729
159148
  }
158730
159149
  async function claimTaskForAutomatedRun(db, taskId, options2) {
@@ -158733,19 +159152,26 @@ async function claimTaskForAutomatedRun(db, taskId, options2) {
158733
159152
  sql: `UPDATE project_tasks
158734
159153
  SET status = ?,
158735
159154
  working = 1,
159155
+ activeRunCount = activeRunCount + 1,
158736
159156
  lastTriggeredAt = ?,
158737
159157
  lastRunStatus = NULL,
158738
159158
  triggerType = CASE WHEN ? = 1 AND scheduledAt IS NOT NULL THEN NULL ELSE triggerType END,
158739
159159
  scheduledAt = CASE WHEN ? = 1 AND scheduledAt IS NOT NULL THEN NULL ELSE scheduledAt END,
158740
159160
  updatedAt = ?
158741
- WHERE id = ? AND working = 0 AND ${enabledClause}`,
159161
+ WHERE id = ?
159162
+ AND ${enabledClause}
159163
+ AND (? = 0 OR scheduledAt IS NOT NULL)
159164
+ AND (? = 0 OR lastTriggeredAt IS ?)`,
158742
159165
  args: [
158743
159166
  options2.status ?? "Build",
158744
159167
  options2.lastTriggeredAt,
158745
159168
  options2.clearSchedule ? 1 : 0,
158746
159169
  options2.clearSchedule ? 1 : 0,
158747
159170
  (/* @__PURE__ */ new Date()).toISOString(),
158748
- taskId
159171
+ taskId,
159172
+ options2.requirePendingSchedule ? 1 : 0,
159173
+ options2.matchLastTriggeredAt ? 1 : 0,
159174
+ options2.expectedLastTriggeredAt ?? null
158749
159175
  ]
158750
159176
  });
158751
159177
  if ((result.rowsAffected ?? 0) === 0) {
@@ -158761,7 +159187,6 @@ async function getDueOneShotScheduleTasks(db, now) {
158761
159187
  AND scheduledAt IS NOT NULL
158762
159188
  AND scheduledAt <= ?
158763
159189
  AND enabled = 1
158764
- AND working = 0
158765
159190
  ORDER BY scheduledAt ASC`,
158766
159191
  args: [now]
158767
159192
  });
@@ -158774,7 +159199,6 @@ async function getRecurringScheduleTasks(db) {
158774
159199
  WHERE triggerType = 'schedule'
158775
159200
  AND scheduleCron IS NOT NULL
158776
159201
  AND enabled = 1
158777
- AND working = 0
158778
159202
  ORDER BY createdAt ASC`,
158779
159203
  args: []
158780
159204
  });
@@ -178664,11 +179088,11 @@ var require_core = __commonJS({
178664
179088
  Ajv2.ValidationError = validation_error_1.default;
178665
179089
  Ajv2.MissingRefError = ref_error_1.default;
178666
179090
  exports2.default = Ajv2;
178667
- function checkOptions(checkOpts, options2, msg, log160 = "error") {
179091
+ function checkOptions(checkOpts, options2, msg, log161 = "error") {
178668
179092
  for (const key in checkOpts) {
178669
179093
  const opt = key;
178670
179094
  if (opt in options2)
178671
- this.logger[log160](`${msg}: option ${key}. ${checkOpts[opt]}`);
179095
+ this.logger[log161](`${msg}: option ${key}. ${checkOpts[opt]}`);
178672
179096
  }
178673
179097
  }
178674
179098
  function getSchEnv(keyRef) {
@@ -217823,7 +218247,7 @@ var require_merge = __commonJS({
217823
218247
  var require_addPairToJSMap = __commonJS({
217824
218248
  "../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports2) {
217825
218249
  "use strict";
217826
- var log160 = require_log();
218250
+ var log161 = require_log();
217827
218251
  var merge3 = require_merge();
217828
218252
  var stringify4 = require_stringify2();
217829
218253
  var identity2 = require_identity();
@@ -217872,7 +218296,7 @@ var require_addPairToJSMap = __commonJS({
217872
218296
  let jsonStr = JSON.stringify(strKey);
217873
218297
  if (jsonStr.length > 40)
217874
218298
  jsonStr = jsonStr.substring(0, 36) + '..."';
217875
- log160.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
218299
+ log161.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
217876
218300
  ctx.mapKeyWarned = true;
217877
218301
  }
217878
218302
  return strKey;
@@ -223288,7 +223712,7 @@ var require_public_api2 = __commonJS({
223288
223712
  var composer = require_composer();
223289
223713
  var Document2 = require_Document();
223290
223714
  var errors = require_errors2();
223291
- var log160 = require_log();
223715
+ var log161 = require_log();
223292
223716
  var identity2 = require_identity();
223293
223717
  var lineCounter = require_line_counter();
223294
223718
  var parser2 = require_parser();
@@ -223340,7 +223764,7 @@ var require_public_api2 = __commonJS({
223340
223764
  const doc = parseDocument(src, options2);
223341
223765
  if (!doc)
223342
223766
  return null;
223343
- doc.warnings.forEach((warning) => log160.warn(doc.options.logLevel, warning));
223767
+ doc.warnings.forEach((warning) => log161.warn(doc.options.logLevel, warning));
223344
223768
  if (doc.errors.length > 0) {
223345
223769
  if (doc.options.logLevel !== "silent")
223346
223770
  throw doc.errors[0];
@@ -229895,10 +230319,13 @@ function estimateTurnCacheCost(previousWire, currentWire) {
229895
230319
  effectiveTokens: cacheReadTokens * CACHE_RATES.read + cacheWriteTokens * CACHE_RATES.writeLong
229896
230320
  };
229897
230321
  }
229898
- function isMicrocompactRewriteCheaperThanKeep(previousWire, uncompacted, compacted) {
230322
+ function isMicrocompactRewriteCheaperThanKeep(previousWire, uncompacted, compacted, observedHitRate) {
229899
230323
  if (!previousWire || previousWire.length === 0) {
229900
230324
  return true;
229901
230325
  }
230326
+ if (observedHitRate !== void 0 && !isPromptCacheUnavailable(observedHitRate) && countLeadingIdenticalMessages(previousWire, compacted) < previousWire.length) {
230327
+ return false;
230328
+ }
229902
230329
  const keep = estimateTurnCacheCost(previousWire, uncompacted);
229903
230330
  const rewrite = estimateTurnCacheCost(previousWire, compacted);
229904
230331
  return rewrite.effectiveTokens <= keep.effectiveTokens;
@@ -229907,6 +230334,7 @@ var CACHE_RATES;
229907
230334
  var init_microcompact_cache_economics = __esm({
229908
230335
  "src/features/microcompact/microcompact-cache-economics.ts"() {
229909
230336
  "use strict";
230337
+ init_dist();
229910
230338
  init_microcompact_tokens();
229911
230339
  CACHE_RATES = {
229912
230340
  read: 0.1,
@@ -230329,6 +230757,10 @@ function pruneSupersededToolResults(messages, options2 = {}) {
230329
230757
  stats.cacheDeferredCount += 1;
230330
230758
  continue;
230331
230759
  }
230760
+ if (options2.cachedPrefixLength !== void 0 && sourceIndex < options2.cachedPrefixLength) {
230761
+ stats.cacheDeferredCount += 1;
230762
+ continue;
230763
+ }
230332
230764
  const marker = supersessionMarker(target.message.toolCallId);
230333
230765
  const replacement = replacementMessage(source2.message, marker);
230334
230766
  if (replacement.tokensSaved <= 0) {
@@ -230392,6 +230824,19 @@ function findLastUserMessageIndex(messages) {
230392
230824
  function resolveCacheHitRate(options2) {
230393
230825
  return options2.cacheHitRateFn ? options2.cacheHitRateFn() : options2.cacheHitRate;
230394
230826
  }
230827
+ function sessionIsReadingPromptCache(cacheHitRate) {
230828
+ return cacheHitRate !== void 0 && !isPromptCacheUnavailable(cacheHitRate);
230829
+ }
230830
+ function isCachedPrefixIndex(options2, index4) {
230831
+ if (options2.overflowRecovery === true) {
230832
+ return false;
230833
+ }
230834
+ if (!sessionIsReadingPromptCache(resolveCacheHitRate(options2))) {
230835
+ return false;
230836
+ }
230837
+ const prefixLength = options2.previousWire?.length ?? 0;
230838
+ return prefixLength > 0 && index4 < prefixLength;
230839
+ }
230395
230840
  function isFrozenToolCallId(frozenToolCallIds, toolCallId) {
230396
230841
  return frozenToolCallIds?.has(toolCallId) === true;
230397
230842
  }
@@ -230793,12 +231238,7 @@ function computeMicrocompactTriggerState(messages, options2, config3) {
230793
231238
  const tokensBefore = sumMessageTokens(messages) + config3.fixedOverheadTokens;
230794
231239
  const overflowRecovery = options2.overflowRecovery === true;
230795
231240
  const noCache = isPromptCacheUnavailable(cacheHitRate);
230796
- const nearWindowCeiling = isNearWindowCeilingTrigger(
230797
- messages,
230798
- options2,
230799
- config3,
230800
- cacheHitRate
230801
- );
231241
+ const nearWindowCeiling = isNearWindowCeilingTrigger(messages, options2, config3, cacheHitRate);
230802
231242
  const proactiveTrigger = isProactiveMicrocompactTrigger(messages, options2);
230803
231243
  const cacheReclaim = isCacheReclaimTrigger(messages, config3, cacheHitRate);
230804
231244
  const frozenToolCallIds = overflowRecovery || noCache ? void 0 : cacheReclaim ? unprotectOldestFrozenIds(
@@ -230909,13 +231349,18 @@ function buildClearedStats(emptyStats2, eligibleCount, keptCount, clearedCount,
230909
231349
  applied: clearedCount > 0
230910
231350
  };
230911
231351
  }
230912
- function logAppliedCompaction(stats, cacheProtectedMessageCount, cacheProtectedTokens) {
231352
+ function cacheRewriteAvoided(previousWireLength, clearCandidates) {
231353
+ return previousWireLength > 0 && clearCandidates.every(function isPastCachedPrefix(index4) {
231354
+ return index4 >= previousWireLength;
231355
+ });
231356
+ }
231357
+ function logAppliedCompaction(stats, cacheProtectedMessageCount, cacheProtectedTokens, clearCandidates, previousWireLength) {
230913
231358
  if (stats.applied) {
230914
231359
  log54.info("Applied micro-compaction", {
230915
231360
  ...stats,
230916
231361
  cacheProtectedMessageCount,
230917
231362
  cacheProtectedTokens,
230918
- cacheRewriteAvoided: cacheProtectedMessageCount > 0
231363
+ cacheRewriteAvoided: cacheRewriteAvoided(previousWireLength, clearCandidates)
230919
231364
  });
230920
231365
  }
230921
231366
  }
@@ -230963,7 +231408,12 @@ function shouldSkipPrefixRewriteForCacheEconomics(trigger, options2, messages, c
230963
231408
  return false;
230964
231409
  }
230965
231410
  const projected = projectClearedMessages(messages, clearCandidates);
230966
- return !isMicrocompactRewriteCheaperThanKeep(previousWire, messages, projected);
231411
+ return !isMicrocompactRewriteCheaperThanKeep(
231412
+ previousWire,
231413
+ messages,
231414
+ projected,
231415
+ trigger.cacheHitRate
231416
+ );
230967
231417
  }
230968
231418
  function isUnprotectedFrozenCandidate(trigger, options2, messages, index4) {
230969
231419
  if (!trigger.nearWindowCeiling && !trigger.cacheReclaim) {
@@ -230978,11 +231428,25 @@ function isUnprotectedFrozenCandidate(trigger, options2, messages, index4) {
230978
231428
  }
230979
231429
  return !isFrozenToolCallId(trigger.frozenToolCallIds, message.toolCallId);
230980
231430
  }
231431
+ function clearCandidatesOutsideCachedPrefix(clearCandidates, prefixLength) {
231432
+ if (prefixLength <= 0) {
231433
+ return clearCandidates;
231434
+ }
231435
+ return clearCandidates.filter(function isOutsideCachedPrefix(index4) {
231436
+ return index4 >= prefixLength;
231437
+ });
231438
+ }
230981
231439
  function selectCacheEconomicClearCandidates(trigger, options2, messages, clearCandidates) {
230982
- if (!shouldSkipPrefixRewriteForCacheEconomics(trigger, options2, messages, clearCandidates)) {
231440
+ if (trigger.noCache || options2.overflowRecovery === true) {
230983
231441
  return clearCandidates;
230984
231442
  }
230985
231443
  const prefixLength = options2.previousWire?.length ?? 0;
231444
+ if (sessionIsReadingPromptCache(trigger.cacheHitRate) && prefixLength > 0) {
231445
+ return clearCandidatesOutsideCachedPrefix(clearCandidates, prefixLength);
231446
+ }
231447
+ if (!shouldSkipPrefixRewriteForCacheEconomics(trigger, options2, messages, clearCandidates)) {
231448
+ return clearCandidates;
231449
+ }
230986
231450
  return clearCandidates.filter(function isAllowedRewrite(index4) {
230987
231451
  return index4 >= prefixLength || isUnprotectedFrozenCandidate(trigger, options2, messages, index4);
230988
231452
  });
@@ -231005,7 +231469,13 @@ async function applyMicrocompactClearance(params) {
231005
231469
  params.tokensBefore,
231006
231470
  tokensAfter
231007
231471
  );
231008
- logAppliedCompaction(stats, params.cacheProtectedMessageCount, params.cacheProtectedTokens);
231472
+ logAppliedCompaction(
231473
+ stats,
231474
+ params.cacheProtectedMessageCount,
231475
+ params.cacheProtectedTokens,
231476
+ params.clearCandidates,
231477
+ params.options.previousWire?.length ?? 0
231478
+ );
231009
231479
  return { messages: next, stats };
231010
231480
  }
231011
231481
  async function microcompactMessages(messages, options2 = {}) {
@@ -231132,6 +231602,9 @@ async function prunePriorTurnToolResults(messages, options2 = {}) {
231132
231602
  let clearedCount = 0;
231133
231603
  let eligibleCount = 0;
231134
231604
  for (let index4 = 0; index4 < lastUserIndex; index4++) {
231605
+ if (isCachedPrefixIndex(options2, index4)) {
231606
+ continue;
231607
+ }
231135
231608
  const candidate = prunablePriorTurnMessage(messages[index4], digestMinChars, frozenToolCallIds);
231136
231609
  if (candidate === null) {
231137
231610
  continue;
@@ -231204,13 +231677,15 @@ function createMicrocompactTransformContext(options2) {
231204
231677
  async function transformContext(messages) {
231205
231678
  const cacheHitRate = resolveCacheHitRate(options2);
231206
231679
  const { protectPrefix, view } = prepareFrozenTransform(options2, messages, cacheHitRate);
231680
+ const previousWire = protectPrefix ? getFrozenPrefixMessages(options2.sessionKey) : void 0;
231207
231681
  const runOptions = {
231208
231682
  ...options2,
231209
231683
  memo,
231210
231684
  frozenToolCallIds: resolveFrozenToolCallIds(protectPrefix, options2),
231211
231685
  cacheHitRate,
231212
231686
  cacheHitRateFn: void 0,
231213
- previousWire: protectPrefix ? getFrozenPrefixMessages(options2.sessionKey) : void 0
231687
+ previousWire,
231688
+ cachedPrefixLength: sessionIsReadingPromptCache(cacheHitRate) && previousWire ? previousWire.length : void 0
231214
231689
  };
231215
231690
  try {
231216
231691
  const deduped = deduplicateIdenticalTodoResults(view);
@@ -231499,9 +231974,9 @@ function extractToolResultText(result) {
231499
231974
  }
231500
231975
  return content.filter((block) => block.type === "text").map((block) => block.text ?? "").join("");
231501
231976
  }
231502
- function logToolCallFailed(log160, event, pending2) {
231977
+ function logToolCallFailed(log161, event, pending2) {
231503
231978
  const errorMessage4 = extractToolResultText(event.result).trim() || "Unknown tool error";
231504
- log160.warn("Tool call failed", {
231979
+ log161.warn("Tool call failed", {
231505
231980
  toolName: event.toolName,
231506
231981
  toolCallId: event.toolCallId,
231507
231982
  arguments: serializeArgumentsForLog(pending2?.args),
@@ -231509,7 +231984,7 @@ function logToolCallFailed(log160, event, pending2) {
231509
231984
  });
231510
231985
  }
231511
231986
  function createAgentEventLogger(event) {
231512
- const log160 = createLogger(event);
231987
+ const log161 = createLogger(event);
231513
231988
  const pendingToolCalls = /* @__PURE__ */ new Map();
231514
231989
  let sequence2 = 0;
231515
231990
  function logPiEvent(piEvent) {
@@ -231525,17 +232000,17 @@ function createAgentEventLogger(event) {
231525
232000
  const pending2 = pendingToolCalls.get(piEvent.toolCallId);
231526
232001
  pendingToolCalls.delete(piEvent.toolCallId);
231527
232002
  if (piEvent.isError) {
231528
- logToolCallFailed(log160, piEvent, pending2);
232003
+ logToolCallFailed(log161, piEvent, pending2);
231529
232004
  }
231530
232005
  }
231531
- log160.debug(`Agent event ${piEvent.type}`, {
232006
+ log161.debug(`Agent event ${piEvent.type}`, {
231532
232007
  sequence: sequence2++,
231533
232008
  get detail() {
231534
232009
  return formatPiAgentEventDetail(piEvent);
231535
232010
  }
231536
232011
  });
231537
232012
  }
231538
- return { log: log160, logPiEvent };
232013
+ return { log: log161, logPiEvent };
231539
232014
  }
231540
232015
  var PI_AGENT_EVENT_DETAIL_MAX_CHARS;
231541
232016
  var init_agent_event_log = __esm({
@@ -232306,11 +232781,33 @@ function createStablePromptCacheKey(model, payload, provider) {
232306
232781
  const key = `tarsk-${digest}`;
232307
232782
  return key.length <= OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH2 ? key : key.slice(0, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH2);
232308
232783
  }
232309
- function applyStablePromptCacheKey(payload, model, provider, sessionId, baseUrl) {
232784
+ function payloadHasCacheablePrefix(payload) {
232785
+ const tools = extractPayloadSections(payload).tools;
232786
+ return tools !== void 0 && tools !== null;
232787
+ }
232788
+ function resolveFrozenPromptCacheKey(model, payload, provider, sessionId, lane = "agent") {
232789
+ const computed = createStablePromptCacheKey(model, payload, provider);
232790
+ if (lane !== "agent" || !sessionId) {
232791
+ return computed;
232792
+ }
232793
+ const frozen = frozenAgentCacheKeyBySession.get(sessionId);
232794
+ if (frozen) {
232795
+ return frozen;
232796
+ }
232797
+ if (payloadHasCacheablePrefix(payload)) {
232798
+ touchTrackedMap(frozenAgentCacheKeyBySession, sessionId, computed);
232799
+ }
232800
+ return computed;
232801
+ }
232802
+ function shouldLogPromptCachePrefixMiss(prefix, hasPrevious) {
232803
+ return hasPrevious && prefix !== void 0 && prefix.appendOnly === false;
232804
+ }
232805
+ function applyStablePromptCacheKey(payload, model, provider, sessionId, baseUrl, lane = "agent") {
232310
232806
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
232311
232807
  return payload;
232312
232808
  }
232313
232809
  const record2 = payload;
232810
+ const resolvedKey = resolveFrozenPromptCacheKey(model, payload, provider, sessionId, lane);
232314
232811
  if (isOpenRouterCacheHost(provider, baseUrl)) {
232315
232812
  const next = { ...record2 };
232316
232813
  delete next.prompt_cache_key;
@@ -232320,12 +232817,13 @@ function applyStablePromptCacheKey(payload, model, provider, sessionId, baseUrl)
232320
232817
  return next;
232321
232818
  }
232322
232819
  if (isOllamaCloudApiUrl(baseUrl)) {
232820
+ const affinityId = lane === "agent" ? resolvedKey : sessionId;
232323
232821
  const next = {
232324
232822
  ...record2,
232325
- prompt_cache_key: createStablePromptCacheKey(model, payload, provider)
232823
+ prompt_cache_key: resolvedKey
232326
232824
  };
232327
- if (typeof sessionId === "string" && sessionId.length > 0) {
232328
- next.session_id = sessionId;
232825
+ if (typeof affinityId === "string" && affinityId.length > 0) {
232826
+ next.session_id = affinityId;
232329
232827
  }
232330
232828
  return next;
232331
232829
  }
@@ -232334,12 +232832,12 @@ function applyStablePromptCacheKey(payload, model, provider, sessionId, baseUrl)
232334
232832
  }
232335
232833
  return {
232336
232834
  ...record2,
232337
- prompt_cache_key: createStablePromptCacheKey(model, payload, provider)
232835
+ prompt_cache_key: resolvedKey
232338
232836
  };
232339
232837
  }
232340
232838
  function applyPromptCacheToProviderPayload(payload, modelId, provider, sessionId, baseUrl, lane = "agent") {
232341
232839
  const cacheSessionId = promptCacheLaneSessionId(sessionId, lane);
232342
- const next = applyStablePromptCacheKey(payload, modelId, provider, cacheSessionId, baseUrl);
232840
+ const next = applyStablePromptCacheKey(payload, modelId, provider, cacheSessionId, baseUrl, lane);
232343
232841
  recordPromptCacheFingerprint(cacheSessionId, provider, modelId, next, baseUrl);
232344
232842
  return next;
232345
232843
  }
@@ -232497,6 +232995,8 @@ function clearPromptCacheSessionState(sessionId) {
232497
232995
  usageBySession.delete(sessionId);
232498
232996
  lastFingerprintBySession.delete(sessionId);
232499
232997
  warnedNoCacheSessions.delete(sessionId);
232998
+ frozenAgentCacheKeyBySession.delete(sessionId);
232999
+ frozenAgentCacheKeyBySession.delete(promptCacheLaneSessionId(sessionId, "compact"));
232500
233000
  for (const key of [...previousBySession.keys()]) {
232501
233001
  if (key.endsWith(`:${sessionId}`)) {
232502
233002
  previousBySession.delete(key);
@@ -232520,7 +233020,9 @@ function recordPromptCacheFingerprint(sessionId, provider, model, payload, baseU
232520
233020
  implicitPromptCache
232521
233021
  });
232522
233022
  const prefix = previous ? comparePromptCacheFingerprints(previous, fingerprint) : void 0;
232523
- log58.debug("Prompt cache fingerprint", {
233023
+ const systemChanged = previous ? previous.system !== fingerprint.system : void 0;
233024
+ const toolsChanged = previous ? previous.tools !== fingerprint.tools : void 0;
233025
+ const fingerprintFields = {
232524
233026
  sessionId,
232525
233027
  provider,
232526
233028
  model,
@@ -232536,17 +233038,22 @@ function recordPromptCacheFingerprint(sessionId, provider, model, payload, baseU
232536
233038
  messageCount: fingerprint.messageHashes.length,
232537
233039
  previousFull: previous?.full,
232538
233040
  previousMessageCount: previous?.messageHashes.length,
232539
- systemChanged: previous ? previous.system !== fingerprint.system : void 0,
232540
- toolsChanged: previous ? previous.tools !== fingerprint.tools : void 0,
233041
+ systemChanged,
233042
+ toolsChanged,
232541
233043
  messagesChanged: previous ? previous.messages !== fingerprint.messages : void 0,
232542
233044
  appendOnly: prefix?.appendOnly,
232543
233045
  reusedMessages: prefix?.reusedMessages,
232544
233046
  firstDivergentIndex: prefix?.firstDivergentIndex,
232545
233047
  divergentLayer: prefix?.divergentLayer
232546
- });
233048
+ };
233049
+ if (shouldLogPromptCachePrefixMiss(prefix, previous !== void 0)) {
233050
+ log58.info("Prompt cache prefix miss", fingerprintFields);
233051
+ } else {
233052
+ log58.debug("Prompt cache fingerprint", fingerprintFields);
233053
+ }
232547
233054
  return fingerprint;
232548
233055
  }
232549
- var log58, MAX_TRACKED_SESSIONS2, previousBySession, messageHashByIdentity, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH2, ZERO_CACHE_MIN_CALLS, ZERO_CACHE_HIT_RATE_THRESHOLD, warnedNoCacheSessions, usageBySession, lastFingerprintBySession;
233056
+ var log58, MAX_TRACKED_SESSIONS2, previousBySession, messageHashByIdentity, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH2, ZERO_CACHE_MIN_CALLS, ZERO_CACHE_HIT_RATE_THRESHOLD, warnedNoCacheSessions, usageBySession, lastFingerprintBySession, frozenAgentCacheKeyBySession;
232550
233057
  var init_prompt_cache_fingerprint = __esm({
232551
233058
  "src/agent/prompt-cache-fingerprint.ts"() {
232552
233059
  "use strict";
@@ -232562,6 +233069,7 @@ var init_prompt_cache_fingerprint = __esm({
232562
233069
  warnedNoCacheSessions = /* @__PURE__ */ new Set();
232563
233070
  usageBySession = /* @__PURE__ */ new Map();
232564
233071
  lastFingerprintBySession = /* @__PURE__ */ new Map();
233072
+ frozenAgentCacheKeyBySession = /* @__PURE__ */ new Map();
232565
233073
  }
232566
233074
  });
232567
233075
 
@@ -239758,6 +240266,16 @@ var init_agent_processing_state_manager = __esm({
239758
240266
  }
239759
240267
  });
239760
240268
 
240269
+ // src/core/dev-mode.ts
240270
+ function isTarskDevelopmentMode(options2, env3 = process.env) {
240271
+ return options2.isDebug || env3.MODE === "development" || env3.TARSK_ELECTROBUN_DEV === "1";
240272
+ }
240273
+ var init_dev_mode = __esm({
240274
+ "src/core/dev-mode.ts"() {
240275
+ "use strict";
240276
+ }
240277
+ });
240278
+
239761
240279
  // src/core/env-manager.ts
239762
240280
  import { promises as fs14 } from "fs";
239763
240281
  import { join as join26 } from "path";
@@ -242337,6 +242855,86 @@ var init_auth_session = __esm({
242337
242855
  }
242338
242856
  });
242339
242857
 
242858
+ // src/features/account/command-confirmation.settings.ts
242859
+ function normalizeAllowAllCommands(raw2) {
242860
+ return raw2 !== false;
242861
+ }
242862
+ function normalizeAllowedCommandPatterns(raw2) {
242863
+ if (typeof raw2 !== "string" || raw2.trim().length === 0) {
242864
+ return [];
242865
+ }
242866
+ try {
242867
+ const parsed = JSON.parse(raw2);
242868
+ if (!Array.isArray(parsed)) {
242869
+ return [];
242870
+ }
242871
+ return parsed.filter((entry) => typeof entry === "string" && entry.length > 0);
242872
+ } catch {
242873
+ return [];
242874
+ }
242875
+ }
242876
+ function mergeAllowedCommandPatterns(projects) {
242877
+ const merged = /* @__PURE__ */ new Set();
242878
+ for (const project of projects) {
242879
+ for (const pattern of normalizeAllowedCommandPatterns(project.allowedCommandPatterns)) {
242880
+ merged.add(pattern);
242881
+ }
242882
+ }
242883
+ return [...merged];
242884
+ }
242885
+ function migrateAllowAllCommands(projects) {
242886
+ if (projects.length === 0) {
242887
+ return true;
242888
+ }
242889
+ return projects.every((project) => project.allowAllCommands !== 0);
242890
+ }
242891
+ async function loadProjectCommandRows(db) {
242892
+ const result = await db.execute("SELECT allowAllCommands, allowedCommandPatterns FROM projects");
242893
+ return result.rows;
242894
+ }
242895
+ async function ensureCommandConfirmationMigrated(db) {
242896
+ const migrated = await getState(db, KEY_COMMAND_CONFIRMATION_MIGRATED);
242897
+ if (migrated === true) {
242898
+ return;
242899
+ }
242900
+ const projects = await loadProjectCommandRows(db);
242901
+ await setState(db, KEY_ALLOW_ALL_COMMANDS, migrateAllowAllCommands(projects));
242902
+ await setState(db, KEY_ALLOWED_COMMAND_PATTERNS, mergeAllowedCommandPatterns(projects));
242903
+ await setState(db, KEY_COMMAND_CONFIRMATION_MIGRATED, true);
242904
+ }
242905
+ async function resolveAllowAllCommands(db) {
242906
+ await ensureCommandConfirmationMigrated(db);
242907
+ const raw2 = await getState(db, KEY_ALLOW_ALL_COMMANDS);
242908
+ return normalizeAllowAllCommands(raw2);
242909
+ }
242910
+ async function resolveAllowedCommandPatterns(db) {
242911
+ await ensureCommandConfirmationMigrated(db);
242912
+ const raw2 = await getState(db, KEY_ALLOWED_COMMAND_PATTERNS);
242913
+ if (Array.isArray(raw2)) {
242914
+ return raw2.filter((entry) => typeof entry === "string" && entry.length > 0);
242915
+ }
242916
+ return normalizeAllowedCommandPatterns(raw2);
242917
+ }
242918
+ async function setAllowAllCommands(db, allowAllCommands) {
242919
+ await ensureCommandConfirmationMigrated(db);
242920
+ await setState(db, KEY_ALLOW_ALL_COMMANDS, allowAllCommands);
242921
+ }
242922
+ async function setAllowedCommandPatterns(db, patterns) {
242923
+ await ensureCommandConfirmationMigrated(db);
242924
+ const normalized = patterns.filter((pattern) => pattern.trim().length > 0);
242925
+ await setState(db, KEY_ALLOWED_COMMAND_PATTERNS, normalized);
242926
+ }
242927
+ var KEY_ALLOW_ALL_COMMANDS, KEY_ALLOWED_COMMAND_PATTERNS, KEY_COMMAND_CONFIRMATION_MIGRATED;
242928
+ var init_command_confirmation_settings = __esm({
242929
+ "src/features/account/command-confirmation.settings.ts"() {
242930
+ "use strict";
242931
+ init_database_state();
242932
+ KEY_ALLOW_ALL_COMMANDS = "AllowAllCommands";
242933
+ KEY_ALLOWED_COMMAND_PATTERNS = "AllowedCommandPatterns";
242934
+ KEY_COMMAND_CONFIRMATION_MIGRATED = "CommandConfirmationMigrated";
242935
+ }
242936
+ });
242937
+
242340
242938
  // src/features/account/account-settings.route.ts
242341
242939
  function isCliDebugMode() {
242342
242940
  return process.argv.includes("--debug");
@@ -242389,7 +242987,7 @@ function normalizeFontScale(value2) {
242389
242987
  }
242390
242988
  return rounded;
242391
242989
  }
242392
- function buildAccountSettingsResponse(rawColorScheme, rawFontScale, rawMaximumTurnCount, rawUseToolsThroughCode, rawVoiceAiProcessing, rawLogging, rawFileLogging, rawBetterStackLogging, rawShowDailySpend, rawSeatbeltSandboxEnabled, serverBindMode, modelAliases) {
242990
+ function buildAccountSettingsResponse(rawColorScheme, rawFontScale, rawMaximumTurnCount, rawUseToolsThroughCode, rawVoiceAiProcessing, rawLogging, rawFileLogging, rawBetterStackLogging, rawShowDailySpend, rawSeatbeltSandboxEnabled, allowAllCommands, allowedCommandPatterns, serverBindMode, modelAliases) {
242393
242991
  const betterStackEnvConfigured = isBetterStackEnvConfigured();
242394
242992
  const loggingEnabled = isLoggingEnabled(rawLogging, rawFileLogging, rawBetterStackLogging);
242395
242993
  const seatbeltHealth = checkSeatbeltSandboxHealth();
@@ -242407,6 +243005,8 @@ function buildAccountSettingsResponse(rawColorScheme, rawFontScale, rawMaximumTu
242407
243005
  seatbeltSandboxSupported: seatbeltHealth.supported,
242408
243006
  seatbeltSandboxHealthy: seatbeltHealth.healthy,
242409
243007
  seatbeltSandboxMessage: seatbeltHealth.message,
243008
+ allowAllCommands,
243009
+ allowedCommandPatterns,
242410
243010
  betterStackLoggingEnabled: loggingEnabled,
242411
243011
  betterStackEnvConfigured,
242412
243012
  betterStackIngestingHost: betterStackEnvConfigured ? getBetterStackIngestingHost() : null,
@@ -242470,6 +243070,8 @@ async function getAccountSettings(c) {
242470
243070
  const rawLogging = await getState(db, KEY_LOGGING);
242471
243071
  const rawShowDailySpend = await getState(db, KEY_SHOW_DAILY_SPEND);
242472
243072
  let rawSeatbeltSandboxEnabled = await getState(db, KEY_SEATBELT_SANDBOX_ENABLED);
243073
+ const allowAllCommands = await resolveAllowAllCommands(db);
243074
+ const allowedCommandPatterns = await resolveAllowedCommandPatterns(db);
242473
243075
  const serverBindMode = await getServerBindModeSetting();
242474
243076
  const modelAliases = await getModelAliases(db);
242475
243077
  if (isSeatbeltSandboxPreferenceEnabled(rawSeatbeltSandboxEnabled)) {
@@ -242488,6 +243090,8 @@ async function getAccountSettings(c) {
242488
243090
  rawBetterStackLogging,
242489
243091
  rawShowDailySpend,
242490
243092
  rawSeatbeltSandboxEnabled,
243093
+ allowAllCommands,
243094
+ allowedCommandPatterns,
242491
243095
  serverBindMode,
242492
243096
  modelAliases
242493
243097
  )
@@ -242583,6 +243187,13 @@ async function applyFontScaleUpdate(db, value2) {
242583
243187
  await setState(db, KEY_FONT_SCALE, normalizeFontScale(value2));
242584
243188
  }
242585
243189
  }
243190
+ async function applyAllowedCommandPatternsUpdate(db, value2) {
243191
+ if (!Array.isArray(value2)) {
243192
+ return;
243193
+ }
243194
+ const patterns = value2.filter((entry) => typeof entry === "string");
243195
+ await setAllowedCommandPatterns(db, patterns);
243196
+ }
242586
243197
  async function buildUpdatedAccountSettingsResponse(db) {
242587
243198
  const rawColorScheme = await getState(db, KEY_COLOR_SCHEME);
242588
243199
  const rawFontScale = await getState(db, KEY_FONT_SCALE);
@@ -242594,6 +243205,8 @@ async function buildUpdatedAccountSettingsResponse(db) {
242594
243205
  const rawLogging = await getState(db, KEY_LOGGING);
242595
243206
  const rawShowDailySpend = await getState(db, KEY_SHOW_DAILY_SPEND);
242596
243207
  const rawSeatbeltSandboxEnabled = await getState(db, KEY_SEATBELT_SANDBOX_ENABLED);
243208
+ const allowAllCommands = await resolveAllowAllCommands(db);
243209
+ const allowedCommandPatterns = await resolveAllowedCommandPatterns(db);
242597
243210
  const serverBindMode = readServerBindMode();
242598
243211
  const modelAliases = await getModelAliases(db);
242599
243212
  return buildAccountSettingsResponse(
@@ -242607,6 +243220,8 @@ async function buildUpdatedAccountSettingsResponse(db) {
242607
243220
  rawBetterStackLogging,
242608
243221
  rawShowDailySpend,
242609
243222
  rawSeatbeltSandboxEnabled,
243223
+ allowAllCommands,
243224
+ allowedCommandPatterns,
242610
243225
  serverBindMode,
242611
243226
  modelAliases
242612
243227
  );
@@ -242623,6 +243238,10 @@ async function updateAccountSettings(c, metadataManager) {
242623
243238
  if (seatbeltError) {
242624
243239
  return c.json(seatbeltError, 400);
242625
243240
  }
243241
+ if (typeof body.allowAllCommands === "boolean") {
243242
+ await setAllowAllCommands(db, body.allowAllCommands);
243243
+ }
243244
+ await applyAllowedCommandPatternsUpdate(db, body.allowedCommandPatterns);
242626
243245
  await applyLoggingUpdate(db, body);
242627
243246
  await applyMaximumTurnCountUpdate(db, body.maximumTurnCount);
242628
243247
  const bindModeError = await applyServerBindModeUpdate(body.serverBindMode);
@@ -242652,6 +243271,7 @@ var init_account_settings_route = __esm({
242652
243271
  init_auth_session();
242653
243272
  init_project_model_aliases();
242654
243273
  init_sandbox();
243274
+ init_command_confirmation_settings();
242655
243275
  DEFAULT_MAXIMUM_TURN_COUNT = 100;
242656
243276
  DEFAULT_FONT_SCALE = 100;
242657
243277
  MIN_FONT_SCALE = 75;
@@ -243241,14 +243861,12 @@ function buildExecutionContext(args2) {
243241
243861
  customSystemPrompt: args2.project?.customSystemPrompt,
243242
243862
  useCustomSystemPrompt: args2.project?.useCustomSystemPrompt,
243243
243863
  projectId: args2.projectId,
243244
- allowAllCommands: args2.slackBranchAutoAllow ? true : args2.project?.allowAllCommands !== false,
243864
+ allowAllCommands: args2.slackBranchAutoAllow ? true : args2.allowAllCommands,
243245
243865
  allowAllFetches: args2.slackBranchAutoAllow,
243246
- allowedCommandPatterns: args2.project?.allowedCommandPatterns ?? [],
243866
+ allowedCommandPatterns: args2.allowedCommandPatterns,
243247
243867
  persistProjectCommandPatterns: async (patterns) => {
243248
243868
  const dbInstance2 = await getDatabase();
243249
- await updateProject(dbInstance2, args2.projectId, {
243250
- allowedCommandPatterns: patterns
243251
- });
243869
+ await setAllowedCommandPatterns(dbInstance2, patterns);
243252
243870
  },
243253
243871
  allowedFetchDomains: args2.project?.allowedFetchDomains ?? [],
243254
243872
  persistProjectFetchDomains: async (domains) => {
@@ -244392,6 +245010,8 @@ async function resolveChatExecution(c, request3, target, metadataManager) {
244392
245010
  let model = baseModel;
244393
245011
  const project = await getProject(db, thread.projectId);
244394
245012
  const seatbeltSandboxEnabled = await resolveSeatbeltSandboxEnabled(db);
245013
+ const allowAllCommands = await resolveAllowAllCommands(db);
245014
+ const allowedCommandPatterns = await resolveAllowedCommandPatterns(db);
244395
245015
  const modelAliases = await getModelAliases(db);
244396
245016
  if (autoRoute) {
244397
245017
  const decision = await routePromptToDecision(userContent, modelAliases, metadataManager);
@@ -244431,6 +245051,8 @@ async function resolveChatExecution(c, request3, target, metadataManager) {
244431
245051
  slackBranchAutoAllow,
244432
245052
  attribution,
244433
245053
  seatbeltSandboxEnabled,
245054
+ allowAllCommands,
245055
+ allowedCommandPatterns,
244434
245056
  smartAliasModelSettings,
244435
245057
  projectId: thread.projectId,
244436
245058
  modelAliases
@@ -244625,6 +245247,8 @@ async function launchGoalRun(c, goal, threadManager, agentExecutor, conversation
244625
245247
  }
244626
245248
  const { provider, model } = resolveProviderAndModel(launchModel.provider, launchModel.model);
244627
245249
  const seatbeltSandboxEnabled = await resolveSeatbeltSandboxEnabled(db);
245250
+ const allowAllCommands = await resolveAllowAllCommands(db);
245251
+ const allowedCommandPatterns = await resolveAllowedCommandPatterns(db);
244628
245252
  const modelAliases = await getModelAliases(db);
244629
245253
  const { availableSkills, activatedSkills, agents } = await loadSkillAndAgentContext(
244630
245254
  thread,
@@ -244647,6 +245271,8 @@ async function launchGoalRun(c, goal, threadManager, agentExecutor, conversation
244647
245271
  project,
244648
245272
  slackBranchAutoAllow: false,
244649
245273
  seatbeltSandboxEnabled,
245274
+ allowAllCommands,
245275
+ allowedCommandPatterns,
244650
245276
  projectId: thread.projectId,
244651
245277
  modelAliases
244652
245278
  });
@@ -244736,6 +245362,7 @@ var init_chat_post_route = __esm({
244736
245362
  init_database();
244737
245363
  init_database_state();
244738
245364
  init_account_settings_route();
245365
+ init_command_confirmation_settings();
244739
245366
  init_model_aliases_store();
244740
245367
  init_project_tasks_database();
244741
245368
  init_git_status_cache_database();
@@ -244805,6 +245432,15 @@ var init_start_turn = __esm({
244805
245432
  });
244806
245433
 
244807
245434
  // src/features/threads/threads-messages.mapper.ts
245435
+ function parseThreadMessageTranscriptView(value2) {
245436
+ if (value2 === void 0 || value2 === "full") {
245437
+ return "full";
245438
+ }
245439
+ if (value2 === "final") {
245440
+ return "final";
245441
+ }
245442
+ return null;
245443
+ }
244808
245444
  function resolveGeneratedImageFields(events, storedImageUrl, rawContent) {
244809
245445
  const storedRefs = storedImageUrl ? parseGeneratedImagePayload(storedImageUrl) ?? { imageUrl: storedImageUrl } : {};
244810
245446
  const eventRefs = extractGeneratedImageFromEvents(events);
@@ -244959,7 +245595,7 @@ function buildMessageMetadata(tokenCount, estimatedCost, processingTimeMs) {
244959
245595
  }
244960
245596
  };
244961
245597
  }
244962
- function buildAgentMessage(message, response, pricingByModelId, threadModelId) {
245598
+ function buildAgentMessage(message, response, pricingByModelId, threadModelId, transcriptView = "full") {
244963
245599
  if (isTarskAgentNoteModel(message.request.model)) {
244964
245600
  const { content: sanitizedContent2 } = sanitizeStructuredMessageContent(response.content);
244965
245601
  return {
@@ -244971,7 +245607,8 @@ function buildAgentMessage(message, response, pricingByModelId, threadModelId) {
244971
245607
  senderLabel: TARSK_AGENT_NOTE_LABEL
244972
245608
  };
244973
245609
  }
244974
- const usageFromEvents = extractUsageFromEvents(response.events);
245610
+ const events = transcriptView === "final" ? [] : response.events;
245611
+ const usageFromEvents = extractUsageFromEvents(events);
244975
245612
  const tokenCount = resolveTokenCount(usageFromEvents, message);
244976
245613
  const estimatedCost = resolveEstimatedCost(
244977
245614
  message,
@@ -244980,13 +245617,9 @@ function buildAgentMessage(message, response, pricingByModelId, threadModelId) {
244980
245617
  threadModelId
244981
245618
  );
244982
245619
  const processingTimeMs = resolveProcessingTimeMs(message, response.completedAt);
244983
- const rawContent = buildStructuredContentFromEvents(response.events, response.content);
245620
+ const rawContent = transcriptView === "final" ? response.content : buildStructuredContentFromEvents(response.events, response.content);
244984
245621
  const { content: sanitizedContent } = sanitizeStructuredMessageContent(rawContent);
244985
- const generatedImage = resolveGeneratedImageFields(
244986
- response.events,
244987
- response.imageUrl,
244988
- rawContent
244989
- );
245622
+ const generatedImage = resolveGeneratedImageFields(events, response.imageUrl, rawContent);
244990
245623
  return {
244991
245624
  messageId: `agent-${message.id}`,
244992
245625
  sender: "agent",
@@ -245006,7 +245639,7 @@ function buildTurnPrefix(message) {
245006
245639
  hasUserMessage: message.request.message.trim().length > 0
245007
245640
  };
245008
245641
  }
245009
- function toThreadMessageResponses(message, pricingByModelId, threadModelId) {
245642
+ function toThreadMessageResponses(message, pricingByModelId, threadModelId, transcriptView = "full") {
245010
245643
  const prefix = buildTurnPrefix(message);
245011
245644
  if (!message.response) {
245012
245645
  return prefix.hasUserMessage ? [prefix.userMessage, ...prefix.steeringMessages] : [...prefix.steeringMessages];
@@ -245015,13 +245648,14 @@ function toThreadMessageResponses(message, pricingByModelId, threadModelId) {
245015
245648
  message,
245016
245649
  message.response,
245017
245650
  pricingByModelId,
245018
- threadModelId
245651
+ threadModelId,
245652
+ transcriptView
245019
245653
  );
245020
245654
  return prefix.hasUserMessage ? [prefix.userMessage, ...prefix.steeringMessages, agentMessage] : [...prefix.steeringMessages, agentMessage];
245021
245655
  }
245022
- function mapConversationMessagesToThreadResponse(messages, pricingByModelId, threadModelId) {
245656
+ function mapConversationMessagesToThreadResponse(messages, pricingByModelId, threadModelId, transcriptView = "full") {
245023
245657
  return messages.flatMap(
245024
- (message) => toThreadMessageResponses(message, pricingByModelId, threadModelId)
245658
+ (message) => toThreadMessageResponses(message, pricingByModelId, threadModelId, transcriptView)
245025
245659
  );
245026
245660
  }
245027
245661
  var init_threads_messages_mapper = __esm({
@@ -245452,9 +246086,6 @@ async function postAgentNote(c, threadManager, conversationManager) {
245452
246086
  await conversationManager.completeMessage(messageId, content, [
245453
246087
  { type: "message", role: "assistant", content }
245454
246088
  ]);
245455
- if (body.markComplete === true) {
245456
- await conversationManager.markConversationComplete(threadId, conversationId);
245457
- }
245458
246089
  const history2 = await conversationManager.getConversationHistoryByConversationId(
245459
246090
  threadId,
245460
246091
  conversationId
@@ -246080,9 +246711,9 @@ var init_conversations_manager = __esm({
246080
246711
  /**
246081
246712
  * Gets the conversation history for a thread (all conversations)
246082
246713
  */
246083
- async getConversationHistory(threadId) {
246714
+ async getConversationHistory(threadId, options2) {
246084
246715
  try {
246085
- return await getConversationHistory(this.db, threadId);
246716
+ return await getConversationHistory(this.db, threadId, options2);
246086
246717
  } catch (error2) {
246087
246718
  log87.error("Failed to get conversation history", { error: error2, threadId });
246088
246719
  throw error2;
@@ -246091,13 +246722,14 @@ var init_conversations_manager = __esm({
246091
246722
  /**
246092
246723
  * Gets the conversation history for a specific conversation
246093
246724
  */
246094
- async getConversationHistoryByConversationId(threadId, conversationId, eventLimit) {
246725
+ async getConversationHistoryByConversationId(threadId, conversationId, eventLimit, options2) {
246095
246726
  try {
246096
246727
  return await getConversationHistoryByConversationId(
246097
246728
  this.db,
246098
246729
  threadId,
246099
246730
  conversationId,
246100
- eventLimit
246731
+ eventLimit,
246732
+ options2
246101
246733
  );
246102
246734
  } catch (error2) {
246103
246735
  log87.error("Failed to get conversation history by conversation ID", {
@@ -246298,7 +246930,23 @@ var init_conversations_manager = __esm({
246298
246930
  try {
246299
246931
  await unmarkConversationComplete(this.db, threadId, conversationId);
246300
246932
  } catch (error2) {
246301
- log87.error("Failed to clear conversation completion", { error: error2, threadId, conversationId });
246933
+ log87.error("Failed to dismiss conversation completion", { error: error2, threadId, conversationId });
246934
+ throw error2;
246935
+ }
246936
+ }
246937
+ async clearThreadCompleteMarks(threadId) {
246938
+ try {
246939
+ await clearThreadCompletionMarks(this.db, threadId);
246940
+ } catch (error2) {
246941
+ log87.error("Failed to clear thread completion marks", { error: error2, threadId });
246942
+ throw error2;
246943
+ }
246944
+ }
246945
+ async getCompletedConversationIds(threadId) {
246946
+ try {
246947
+ return await getCompletedConversationIds(this.db, threadId);
246948
+ } catch (error2) {
246949
+ log87.error("Failed to get completed conversation IDs", { error: error2, threadId });
246302
246950
  throw error2;
246303
246951
  }
246304
246952
  }
@@ -247177,6 +247825,60 @@ var init_conversations_clear_complete_route = __esm({
247177
247825
  }
247178
247826
  });
247179
247827
 
247828
+ // src/features/conversations/conversations-get-completions.route.ts
247829
+ async function getCompletedConversationIds2(c, conversationManager, threadManager) {
247830
+ try {
247831
+ const thread = await requireExistingThread(c, threadManager);
247832
+ if (!thread.ok) {
247833
+ return thread.response;
247834
+ }
247835
+ const conversationIds = await conversationManager.getCompletedConversationIds(thread.threadId);
247836
+ return successResponse(c, { conversationIds }, 200);
247837
+ } catch (error2) {
247838
+ return caughtErrorResponse(
247839
+ c,
247840
+ ErrorCodes.HISTORY_FETCH_ERROR,
247841
+ "Failed to get completed conversations",
247842
+ error2
247843
+ );
247844
+ }
247845
+ }
247846
+ var init_conversations_get_completions_route = __esm({
247847
+ "src/features/conversations/conversations-get-completions.route.ts"() {
247848
+ "use strict";
247849
+ init_error_responses();
247850
+ init_route_helpers();
247851
+ init_conversation_route_params();
247852
+ }
247853
+ });
247854
+
247855
+ // src/features/conversations/conversations-clear-thread-complete.route.ts
247856
+ async function clearThreadCompleteMarks(c, conversationManager, threadManager) {
247857
+ try {
247858
+ const thread = await requireExistingThread(c, threadManager);
247859
+ if (!thread.ok) {
247860
+ return thread.response;
247861
+ }
247862
+ await conversationManager.clearThreadCompleteMarks(thread.threadId);
247863
+ return successResponse(c, { threadId: thread.threadId }, 200);
247864
+ } catch (error2) {
247865
+ return caughtErrorResponse(
247866
+ c,
247867
+ ErrorCodes.HISTORY_FETCH_ERROR,
247868
+ "Failed to clear thread completion marks",
247869
+ error2
247870
+ );
247871
+ }
247872
+ }
247873
+ var init_conversations_clear_thread_complete_route = __esm({
247874
+ "src/features/conversations/conversations-clear-thread-complete.route.ts"() {
247875
+ "use strict";
247876
+ init_error_responses();
247877
+ init_route_helpers();
247878
+ init_conversation_route_params();
247879
+ }
247880
+ });
247881
+
247180
247882
  // src/features/conversations/conversations-export.route.ts
247181
247883
  function jsonlResponse(body, filename) {
247182
247884
  return new Response(body, {
@@ -247667,6 +248369,10 @@ function createConversationRoutes(conversationManager, threadManager, metadataMa
247667
248369
  "/:threadId/usage-summary",
247668
248370
  (c) => handleGetThreadUsageSummary(c, conversationManager, threadManager)
247669
248371
  );
248372
+ router.get(
248373
+ "/:threadId/completions",
248374
+ (c) => getCompletedConversationIds2(c, conversationManager, threadManager)
248375
+ );
247670
248376
  router.get(
247671
248377
  "/:threadId/:conversationId/usage",
247672
248378
  (c) => handleGetConversationUsage(c, conversationManager, threadManager)
@@ -247720,6 +248426,10 @@ function createConversationRoutes(conversationManager, threadManager, metadataMa
247720
248426
  "/:threadId/:conversationId/complete",
247721
248427
  (c) => clearConversationComplete(c, conversationManager, threadManager)
247722
248428
  );
248429
+ router.delete(
248430
+ "/:threadId/complete",
248431
+ (c) => clearThreadCompleteMarks(c, conversationManager, threadManager)
248432
+ );
247723
248433
  router.delete(
247724
248434
  "/:threadId/:conversationId",
247725
248435
  (c) => deleteConversation(c, conversationManager, threadManager)
@@ -247746,6 +248456,8 @@ var init_conversations_routes = __esm({
247746
248456
  init_conversations_unarchive_route();
247747
248457
  init_conversations_sibling_route();
247748
248458
  init_conversations_clear_complete_route();
248459
+ init_conversations_get_completions_route();
248460
+ init_conversations_clear_thread_complete_route();
247749
248461
  init_conversations_export_route();
247750
248462
  init_token_usage_route();
247751
248463
  init_context_breakdown_route();
@@ -263388,12 +264100,19 @@ async function handleGetThreadMessages(c, threadManager, conversationManager) {
263388
264100
  return existing.response;
263389
264101
  }
263390
264102
  const { threadId, thread } = existing;
264103
+ const transcriptView = parseThreadMessageTranscriptView(c.req.query("view"));
264104
+ if (!transcriptView) {
264105
+ return errorResponse(c, ErrorCodes.INVALID_REQUEST, "view must be full or final", 400);
264106
+ }
264107
+ const historyRead = transcriptView === "final" ? { omitEvents: true } : void 0;
263391
264108
  const requestedConversationId = c.req.query("conversationId");
263392
264109
  const targetConversationId = typeof requestedConversationId === "string" && requestedConversationId.length > 0 ? requestedConversationId : thread.currentConversationId;
263393
264110
  const history2 = targetConversationId ? await conversationManager.getConversationHistoryByConversationId(
263394
264111
  threadId,
263395
- targetConversationId
263396
- ) : await conversationManager.getConversationHistory(threadId);
264112
+ targetConversationId,
264113
+ void 0,
264114
+ historyRead
264115
+ ) : await conversationManager.getConversationHistory(threadId, historyRead);
263397
264116
  if (!history2 || history2.messages.length === 0) {
263398
264117
  return c.json([]);
263399
264118
  }
@@ -263403,7 +264122,12 @@ async function handleGetThreadMessages(c, threadManager, conversationManager) {
263403
264122
  );
263404
264123
  const pricingByModelId = await buildPricingByModelId(providerIds);
263405
264124
  return c.json(
263406
- mapConversationMessagesToThreadResponse(history2.messages, pricingByModelId, thread.model)
264125
+ mapConversationMessagesToThreadResponse(
264126
+ history2.messages,
264127
+ pricingByModelId,
264128
+ thread.model,
264129
+ transcriptView
264130
+ )
263407
264131
  );
263408
264132
  } catch (error2) {
263409
264133
  return caughtErrorResponse(
@@ -332832,8 +333556,8 @@ var debug2;
332832
333556
  var init_debug = __esm({
332833
333557
  "../node_modules/.bun/@puppeteer+browsers@3.0.5+b412256286331130/node_modules/@puppeteer/browsers/lib/debug.js"() {
332834
333558
  debug2 = (prefix) => {
332835
- const log160 = debuglog2(prefix);
332836
- return log160.enabled ? log160 : void 0;
333559
+ const log161 = debuglog2(prefix);
333560
+ return log161.enabled ? log161 : void 0;
332837
333561
  };
332838
333562
  }
332839
333563
  });
@@ -417227,17 +417951,17 @@ async function resolveGitThreadContext(c, metadataManager, options2 = {}) {
417227
417951
  };
417228
417952
  }
417229
417953
  }
417230
- async function resolveGitBranchContext(c, metadataManager, log160) {
417954
+ async function resolveGitBranchContext(c, metadataManager, log161) {
417231
417955
  const resolved = await resolveGitThreadContext(c, metadataManager);
417232
417956
  if (!resolved.ok) {
417233
417957
  return resolved;
417234
417958
  }
417235
417959
  const { threadId, thread, absolutePath, gitRoot } = resolved.context;
417236
- log160.info("Thread found", { threadId, title: thread.title });
417237
- log160.info("Resolved thread path", { absolutePath });
417238
- log160.info("Resolved git root", { gitRoot });
417960
+ log161.info("Thread found", { threadId, title: thread.title });
417961
+ log161.info("Resolved thread path", { absolutePath });
417962
+ log161.info("Resolved git root", { gitRoot });
417239
417963
  const currentBranch = await getCurrentBranch(gitRoot);
417240
- log160.info("Resolved current branch", { currentBranch });
417964
+ log161.info("Resolved current branch", { currentBranch });
417241
417965
  return {
417242
417966
  ok: true,
417243
417967
  context: { ...resolved.context, currentBranch }
@@ -421035,11 +421759,23 @@ var init_agents_routes = __esm({
421035
421759
 
421036
421760
  // src/features/project-tasks/task-execution.ts
421037
421761
  function claimFailureMessage(task) {
421038
- if (task.working) {
421039
- return "Task is already running";
421040
- }
421041
421762
  return `Failed to claim task: ${task.id}`;
421042
421763
  }
421764
+ function isScheduleOneShot(task) {
421765
+ return Boolean(task.scheduledAt?.trim());
421766
+ }
421767
+ function isBenignClaimMiss(before, after, guards) {
421768
+ if (!after) {
421769
+ return false;
421770
+ }
421771
+ if (guards.requirePendingSchedule && !after.scheduledAt) {
421772
+ return true;
421773
+ }
421774
+ if (guards.matchLastTriggeredAt && after.lastTriggeredAt !== before.lastTriggeredAt) {
421775
+ return true;
421776
+ }
421777
+ return false;
421778
+ }
421043
421779
  async function loadAndClaimTask(taskId, options2) {
421044
421780
  const db = await getDatabase();
421045
421781
  const task = await getTaskById(db, taskId);
@@ -421047,13 +421783,22 @@ async function loadAndClaimTask(taskId, options2) {
421047
421783
  throw new Error(`Task not found: ${taskId}`);
421048
421784
  }
421049
421785
  const now = (/* @__PURE__ */ new Date()).toISOString();
421786
+ const requirePendingSchedule = options2.trigger === "schedule" && isScheduleOneShot(task);
421787
+ const matchLastTriggeredAt = options2.trigger === "schedule" && Boolean(task.scheduleCron);
421050
421788
  const claimed = await claimTaskForAutomatedRun(db, task.id, {
421051
421789
  status: "Build",
421052
421790
  lastTriggeredAt: now,
421053
421791
  clearSchedule: options2.clearSchedule,
421054
- requireEnabled: options2.requireEnabled
421792
+ requireEnabled: options2.requireEnabled,
421793
+ requirePendingSchedule,
421794
+ matchLastTriggeredAt,
421795
+ expectedLastTriggeredAt: task.lastTriggeredAt
421055
421796
  });
421056
421797
  if (!claimed) {
421798
+ const current = await getTaskById(db, taskId);
421799
+ if (isBenignClaimMiss(task, current, { requirePendingSchedule, matchLastTriggeredAt })) {
421800
+ throw new TaskRunSkippedError(task.id);
421801
+ }
421057
421802
  throw new Error(claimFailureMessage(task));
421058
421803
  }
421059
421804
  return { db, task, now };
@@ -421096,6 +421841,7 @@ async function attachThreadToTask(db, task, createdThreadId, now) {
421096
421841
  if (!taskWithThread) {
421097
421842
  throw new Error(`Failed to attach thread to task: ${task.id}`);
421098
421843
  }
421844
+ await registerActiveTaskRun(db, task.id, createdThreadId);
421099
421845
  return taskWithThread;
421100
421846
  }
421101
421847
  function chatLaunchErrorMessage(response) {
@@ -421148,16 +421894,16 @@ async function markTaskLaunchSucceeded(db, taskId) {
421148
421894
  }
421149
421895
  return launched;
421150
421896
  }
421151
- async function restoreTaskAfterFailure(db, task, error2) {
421897
+ async function restoreTaskAfterFailure(db, task, error2, threadId) {
421152
421898
  log137.error("Automated task run failed", {
421153
421899
  taskId: task.id,
421154
421900
  projectId: task.projectId,
421155
421901
  title: task.title,
421156
421902
  error: error2
421157
421903
  });
421904
+ await releaseAutomatedRun(db, task.id, threadId);
421158
421905
  const currentTask = await getTaskById(db, task.id);
421159
421906
  const restored = await updateTask(db, task.id, {
421160
- working: false,
421161
421907
  threadId: currentTask?.threadId ?? task.threadId,
421162
421908
  lastRunStatus: "failed"
421163
421909
  });
@@ -421167,14 +421913,15 @@ async function restoreTaskAfterFailure(db, task, error2) {
421167
421913
  }
421168
421914
  async function executeTaskInNewThread(taskId, deps2, options2 = {}) {
421169
421915
  const { db, task, now } = await loadAndClaimTask(taskId, options2);
421916
+ let createdThreadId = null;
421170
421917
  try {
421171
- const createdThreadId = await createThreadForTask(task, deps2);
421918
+ createdThreadId = await createThreadForTask(task, deps2);
421172
421919
  await attachThreadToTask(db, task, createdThreadId, now);
421173
421920
  await launchTaskChat(task, createdThreadId, deps2, options2);
421174
421921
  await recordTaskRunHistory(db, task, createdThreadId, options2);
421175
421922
  return markTaskLaunchSucceeded(db, task.id);
421176
421923
  } catch (error2) {
421177
- await restoreTaskAfterFailure(db, task, error2);
421924
+ await restoreTaskAfterFailure(db, task, error2, createdThreadId);
421178
421925
  throw error2;
421179
421926
  }
421180
421927
  }
@@ -421223,7 +421970,7 @@ function promptAlreadyInvokesSkill(description, skillRef) {
421223
421970
  (reference) => reference.name === normalizedReference
421224
421971
  );
421225
421972
  }
421226
- var log137;
421973
+ var log137, TaskRunSkippedError;
421227
421974
  var init_task_execution = __esm({
421228
421975
  "src/features/project-tasks/task-execution.ts"() {
421229
421976
  "use strict";
@@ -421235,6 +421982,12 @@ var init_task_execution = __esm({
421235
421982
  init_task_default_model();
421236
421983
  init_project_tasks_database();
421237
421984
  log137 = createLogger("task.execution");
421985
+ TaskRunSkippedError = class extends Error {
421986
+ constructor(taskId) {
421987
+ super(`Task run already claimed: ${taskId}`);
421988
+ this.name = "TaskRunSkippedError";
421989
+ }
421990
+ };
421238
421991
  }
421239
421992
  });
421240
421993
 
@@ -439818,13 +440571,13 @@ var require_mock_call_history = __commonJS({
439818
440571
  function makeFilterCalls(parameterName) {
439819
440572
  return (parameterValue, logs) => {
439820
440573
  if (typeof parameterValue === "string" || parameterValue == null) {
439821
- return logs.filter((log160) => {
439822
- return log160[parameterName] === parameterValue;
440574
+ return logs.filter((log161) => {
440575
+ return log161[parameterName] === parameterValue;
439823
440576
  });
439824
440577
  }
439825
440578
  if (parameterValue instanceof RegExp) {
439826
- return logs.filter((log160) => {
439827
- return parameterValue.test(log160[parameterName]);
440579
+ return logs.filter((log161) => {
440580
+ return parameterValue.test(log161[parameterName]);
439828
440581
  });
439829
440582
  }
439830
440583
  throw new InvalidArgumentError(`${parameterName} parameter should be one of string, regexp, undefined or null`);
@@ -439919,8 +440672,8 @@ var require_mock_call_history = __commonJS({
439919
440672
  return this.logs.filter(criteria);
439920
440673
  }
439921
440674
  if (criteria instanceof RegExp) {
439922
- return this.logs.filter((log160) => {
439923
- return criteria.test(log160.toString());
440675
+ return this.logs.filter((log161) => {
440676
+ return criteria.test(log161.toString());
439924
440677
  });
439925
440678
  }
439926
440679
  if (typeof criteria === "object" && criteria !== null) {
@@ -439970,13 +440723,13 @@ var require_mock_call_history = __commonJS({
439970
440723
  this.logs = [];
439971
440724
  }
439972
440725
  [kMockCallHistoryAddLog](requestInit) {
439973
- const log160 = new MockCallHistoryLog(requestInit);
439974
- this.logs.push(log160);
439975
- return log160;
440726
+ const log161 = new MockCallHistoryLog(requestInit);
440727
+ this.logs.push(log161);
440728
+ return log161;
439976
440729
  }
439977
440730
  *[Symbol.iterator]() {
439978
- for (const log160 of this.calls()) {
439979
- yield log160;
440731
+ for (const log161 of this.calls()) {
440732
+ yield log161;
439980
440733
  }
439981
440734
  }
439982
440735
  };
@@ -458736,19 +459489,19 @@ async function pollDueTasks(deps2, now = /* @__PURE__ */ new Date()) {
458736
459489
  const db = await getDatabase();
458737
459490
  const nowIso = now.toISOString();
458738
459491
  const oneShotTasks = await getDueOneShotScheduleTasks(db, nowIso);
458739
- for (const row of oneShotTasks) {
458740
- await runScheduledTask(row.id, deps2, row);
458741
- }
458742
459492
  const recurringTasks = await getRecurringScheduleTasks(db);
458743
- for (const row of recurringTasks) {
459493
+ const dueRecurringTasks = recurringTasks.filter(function isDueRecurringTask(row) {
458744
459494
  if (!row.scheduleCron || !row.timezone) {
458745
- continue;
458746
- }
458747
- if (!isCronDue(row.scheduleCron, row.timezone, row.lastTriggeredAt, now)) {
458748
- continue;
459495
+ return false;
458749
459496
  }
458750
- await runScheduledTask(row.id, deps2, row);
458751
- }
459497
+ return isCronDue(row.scheduleCron, row.timezone, row.lastTriggeredAt, now);
459498
+ });
459499
+ const dueTasks = [...oneShotTasks, ...dueRecurringTasks];
459500
+ await Promise.all(
459501
+ dueTasks.map(function launchDueTask(row) {
459502
+ return runScheduledTask(row.id, deps2, row);
459503
+ })
459504
+ );
458752
459505
  }
458753
459506
  async function runScheduledTask(taskId, deps2, task) {
458754
459507
  try {
@@ -458771,6 +459524,9 @@ async function runScheduledTask(taskId, deps2, task) {
458771
459524
  lastRunStatus: launched.lastRunStatus
458772
459525
  });
458773
459526
  } catch (error2) {
459527
+ if (error2 instanceof Error && error2.name === "TaskRunSkippedError") {
459528
+ return;
459529
+ }
458774
459530
  log157.error("Failed to run scheduled task", {
458775
459531
  error: error2,
458776
459532
  taskId,
@@ -458794,19 +459550,127 @@ var init_task_scheduler = __esm({
458794
459550
  }
458795
459551
  });
458796
459552
 
459553
+ // src/features/git/pr-merged-scanner.ts
459554
+ function startPrMergedScanner(deps2) {
459555
+ const timer2 = setInterval(() => {
459556
+ void scanForMergedPullRequests(deps2);
459557
+ }, SCAN_INTERVAL_MS);
459558
+ void scanForMergedPullRequests(deps2);
459559
+ return timer2;
459560
+ }
459561
+ async function scanForMergedPullRequests(deps2) {
459562
+ try {
459563
+ const db = await getDatabase();
459564
+ const threads = await deps2.metadataManager.loadThreads();
459565
+ for (const thread of threads) {
459566
+ try {
459567
+ await scanThreadForMergedPullRequest(db, deps2, thread);
459568
+ } catch (error2) {
459569
+ log158.warn("Merged-PR scan skipped a thread after an error", {
459570
+ threadId: thread.id,
459571
+ branch: thread.currentBranch,
459572
+ error: error2
459573
+ });
459574
+ }
459575
+ }
459576
+ } catch (error2) {
459577
+ log158.error("Merged-PR scan failed", { error: error2 });
459578
+ }
459579
+ }
459580
+ async function resolveScanContext(deps2, thread) {
459581
+ const branch = thread.currentBranch;
459582
+ if (!branch || branch === "HEAD") {
459583
+ return null;
459584
+ }
459585
+ if (deps2.processingStateManager.isProcessing(thread.id)) {
459586
+ return null;
459587
+ }
459588
+ let gitRoot;
459589
+ try {
459590
+ gitRoot = await getGitRoot(resolveThreadPath(thread.path ?? ""));
459591
+ } catch {
459592
+ return null;
459593
+ }
459594
+ const defaultBranch = await findDefaultBranchCached(gitRoot).catch(() => null);
459595
+ if (!defaultBranch || branch === defaultBranch) {
459596
+ return null;
459597
+ }
459598
+ return { gitRoot, branch };
459599
+ }
459600
+ async function getMergedPrUrl(gitRoot, branch) {
459601
+ const prStatus = await getPullRequestStatusCached(gitRoot, branch, false).catch(() => null);
459602
+ if (!prStatus?.exists || prStatus.state !== "MERGED" || !prStatus.url) {
459603
+ return null;
459604
+ }
459605
+ return prStatus.url;
459606
+ }
459607
+ async function markUnmarkedConversations(db, thread, branch, prUrl) {
459608
+ const [mergeable, completionRows] = await Promise.all([
459609
+ getMergeableConversationIds(db, thread.id),
459610
+ getConversationCompletionRows(db, thread.id)
459611
+ ]);
459612
+ const knownConversationIds = new Set(completionRows.map((row) => row.conversationId));
459613
+ const unmarked = mergeable.filter((conversationId) => !knownConversationIds.has(conversationId));
459614
+ if (unmarked.length === 0) {
459615
+ return;
459616
+ }
459617
+ for (const conversationId of unmarked) {
459618
+ await markConversationComplete(db, thread.id, conversationId);
459619
+ }
459620
+ const markedCount = unmarked.length;
459621
+ const previous = scanEchoes.get(thread.id);
459622
+ if (!previous || previous.prUrl !== prUrl || previous.markedCount !== markedCount) {
459623
+ log158.info("Marked chats as merged", {
459624
+ threadId: thread.id,
459625
+ projectId: thread.projectId,
459626
+ branch,
459627
+ prUrl,
459628
+ markedCount
459629
+ });
459630
+ scanEchoes.set(thread.id, { branch, prUrl, markedCount });
459631
+ }
459632
+ }
459633
+ async function scanThreadForMergedPullRequest(db, deps2, thread) {
459634
+ const context2 = await resolveScanContext(deps2, thread);
459635
+ if (!context2) {
459636
+ return;
459637
+ }
459638
+ const prUrl = await getMergedPrUrl(context2.gitRoot, context2.branch);
459639
+ if (!prUrl) {
459640
+ scanEchoes.delete(thread.id);
459641
+ return;
459642
+ }
459643
+ await markUnmarkedConversations(db, thread, context2.branch, prUrl);
459644
+ }
459645
+ var log158, SCAN_INTERVAL_MS, scanEchoes;
459646
+ var init_pr_merged_scanner = __esm({
459647
+ "src/features/git/pr-merged-scanner.ts"() {
459648
+ "use strict";
459649
+ init_dist();
459650
+ init_database();
459651
+ init_conversations_database();
459652
+ init_git_utils();
459653
+ log158 = createLogger("git.prMergedScanner");
459654
+ SCAN_INTERVAL_MS = 6e4;
459655
+ scanEchoes = /* @__PURE__ */ new Map();
459656
+ }
459657
+ });
459658
+
458797
459659
  // src/features/inbound-events/inbound-events.service.ts
458798
459660
  async function dispatchProjectEvent(db, projectId, eventKey, payload, deps2) {
458799
459661
  const matches = await getEnabledEventTasksByKey(db, projectId, eventKey);
458800
- let launched = 0;
458801
- let skipped = 0;
458802
- for (const task of matches) {
458803
- try {
458804
- await launchEventTask(task, deps2, eventKey, payload);
458805
- launched += 1;
458806
- } catch {
458807
- skipped += 1;
458808
- }
458809
- }
459662
+ const outcomes = await Promise.all(
459663
+ matches.map(async function launchMatchedTask(task) {
459664
+ try {
459665
+ await launchEventTask(task, deps2, eventKey, payload);
459666
+ return "launched";
459667
+ } catch {
459668
+ return "skipped";
459669
+ }
459670
+ })
459671
+ );
459672
+ const launched = outcomes.filter((outcome) => outcome === "launched").length;
459673
+ const skipped = outcomes.length - launched;
458810
459674
  return {
458811
459675
  matched: matches.length,
458812
459676
  launched,
@@ -458927,7 +459791,7 @@ var init_inbound_events_routes = __esm({
458927
459791
  async function openScreenRecordingSettings() {
458928
459792
  if (process.platform !== "darwin") {
458929
459793
  const message = "Screen recording settings are only available on macOS.";
458930
- log158.warn(`[ScreenRecordingSettings] ${message}`);
459794
+ log159.warn(`[ScreenRecordingSettings] ${message}`);
458931
459795
  return {
458932
459796
  success: false,
458933
459797
  message,
@@ -458935,8 +459799,8 @@ async function openScreenRecordingSettings() {
458935
459799
  };
458936
459800
  }
458937
459801
  const settingsUrl = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
458938
- log158.info(`[ScreenRecordingSettings] Opening System Settings to Screen Recording pane...`);
458939
- log158.info(`[ScreenRecordingSettings] URL: ${settingsUrl}`);
459802
+ log159.info(`[ScreenRecordingSettings] Opening System Settings to Screen Recording pane...`);
459803
+ log159.info(`[ScreenRecordingSettings] URL: ${settingsUrl}`);
458940
459804
  try {
458941
459805
  const { shell, args: args2 } = getShellConfig();
458942
459806
  const result = spawnSyncProcess(shell, [...args2, `open "${settingsUrl}"`], {
@@ -458945,17 +459809,17 @@ async function openScreenRecordingSettings() {
458945
459809
  timeout: 5e3
458946
459810
  });
458947
459811
  if (result.status === 0) {
458948
- log158.info(`[ScreenRecordingSettings] Successfully opened System Settings`);
459812
+ log159.info(`[ScreenRecordingSettings] Successfully opened System Settings`);
458949
459813
  return {
458950
459814
  success: true,
458951
459815
  message: "System Settings opened to Screen & Audio Recording pane."
458952
459816
  };
458953
459817
  } else {
458954
459818
  const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "Unknown error";
458955
- log158.error(
459819
+ log159.error(
458956
459820
  `[ScreenRecordingSettings] Failed to open System Settings. Exit code: ${result.status}`
458957
459821
  );
458958
- log158.error(`[ScreenRecordingSettings] stderr: ${stderr}`);
459822
+ log159.error(`[ScreenRecordingSettings] stderr: ${stderr}`);
458959
459823
  return {
458960
459824
  success: false,
458961
459825
  message: "Failed to open System Settings. Please open it manually: System Settings > Privacy & Security > Screen & Audio Recording.",
@@ -458964,7 +459828,7 @@ async function openScreenRecordingSettings() {
458964
459828
  }
458965
459829
  } catch (error2) {
458966
459830
  const errorMessage4 = error2 instanceof Error ? error2.message : String(error2);
458967
- log158.error(`[ScreenRecordingSettings] Exception while opening System Settings:`, error2);
459831
+ log159.error(`[ScreenRecordingSettings] Exception while opening System Settings:`, error2);
458968
459832
  return {
458969
459833
  success: false,
458970
459834
  message: "Failed to open System Settings. Please open it manually: System Settings > Privacy & Security > Screen & Audio Recording.",
@@ -458972,14 +459836,14 @@ async function openScreenRecordingSettings() {
458972
459836
  };
458973
459837
  }
458974
459838
  }
458975
- var log158;
459839
+ var log159;
458976
459840
  var init_screen_recording_settings_opener = __esm({
458977
459841
  "src/features/system-permissions/screen-recording-settings-opener.ts"() {
458978
459842
  "use strict";
458979
459843
  init_utils();
458980
459844
  init_shell();
458981
459845
  init_dist();
458982
- log158 = createLogger("features.system-permissions.screen-recording-settings-opener");
459846
+ log159 = createLogger("features.system-permissions.screen-recording-settings-opener");
458983
459847
  }
458984
459848
  });
458985
459849
 
@@ -459223,7 +460087,7 @@ async function bindTarskListeningServer(options2) {
459223
460087
  url: `http://localhost:${server.port ?? port}`,
459224
460088
  port: server.port ?? port,
459225
460089
  bound: true,
459226
- isDevMode: (server.port ?? port) !== options2.initialPort
460090
+ isDevMode: options2.isDevMode
459227
460091
  };
459228
460092
  } catch (error2) {
459229
460093
  const errno = error2;
@@ -459260,7 +460124,7 @@ async function bindTarskListeningServer(options2) {
459260
460124
  url: `http://localhost:${port}`,
459261
460125
  port,
459262
460126
  bound: true,
459263
- isDevMode: port !== options2.initialPort
460127
+ isDevMode: options2.isDevMode
459264
460128
  };
459265
460129
  }
459266
460130
  };
@@ -459277,7 +460141,7 @@ function registerProcessExitHandler() {
459277
460141
  });
459278
460142
  }
459279
460143
  function shutdownTarskServer() {
459280
- log159.info("Shutting down Tarsk server...");
460144
+ log160.info("Shutting down Tarsk server...");
459281
460145
  resetTurnDispatch();
459282
460146
  try {
459283
460147
  if (activeProcessingStateManager) {
@@ -459289,27 +460153,27 @@ function shutdownTarskServer() {
459289
460153
  }
459290
460154
  }
459291
460155
  } catch (error2) {
459292
- log159.warn("Failed to abort in-flight agent runs during shutdown", { error: error2 });
460156
+ log160.warn("Failed to abort in-flight agent runs during shutdown", { error: error2 });
459293
460157
  }
459294
460158
  try {
459295
460159
  activePtySessionManager?.shutdown();
459296
460160
  } catch (error2) {
459297
- log159.warn("Failed to stop PTY sessions during shutdown", { error: error2 });
460161
+ log160.warn("Failed to stop PTY sessions during shutdown", { error: error2 });
459298
460162
  }
459299
460163
  try {
459300
460164
  scriptProcessManager.stopAll();
459301
460165
  } catch (error2) {
459302
- log159.warn("Failed to stop running scripts during shutdown", { error: error2 });
460166
+ log160.warn("Failed to stop running scripts during shutdown", { error: error2 });
459303
460167
  }
459304
460168
  try {
459305
460169
  stopPublicTunnel();
459306
460170
  } catch (error2) {
459307
- log159.warn("Failed to stop the public address during shutdown", { error: error2 });
460171
+ log160.warn("Failed to stop the public address during shutdown", { error: error2 });
459308
460172
  }
459309
460173
  try {
459310
460174
  cliProcessRegistry.killAll();
459311
460175
  } catch (error2) {
459312
- log159.warn("Failed to kill tracked child processes during shutdown", { error: error2 });
460176
+ log160.warn("Failed to kill tracked child processes during shutdown", { error: error2 });
459313
460177
  }
459314
460178
  try {
459315
460179
  devServerCache.clearAll();
@@ -459328,9 +460192,16 @@ function shutdownTarskServer() {
459328
460192
  }
459329
460193
  activeTaskSchedulerTimer = null;
459330
460194
  }
460195
+ if (activePrMergedScannerTimer) {
460196
+ try {
460197
+ clearInterval(activePrMergedScannerTimer);
460198
+ } catch {
460199
+ }
460200
+ activePrMergedScannerTimer = null;
460201
+ }
459331
460202
  void stopSlackBridge();
459332
460203
  startPromise = null;
459333
- log159.info("Tarsk server shutdown complete.");
460204
+ log160.info("Tarsk server shutdown complete.");
459334
460205
  }
459335
460206
  function registerShutdownSignalHandlers() {
459336
460207
  if (signalHandlersRegistered) {
@@ -459362,7 +460233,7 @@ function resolveInitialServerPort(isDebug2) {
459362
460233
  }
459363
460234
  function mountServerLogging(app, initialPort) {
459364
460235
  app.onError((err2, c) => {
459365
- log159.error("Unhandled API route error", {
460236
+ log160.error("Unhandled API route error", {
459366
460237
  method: c.req.method,
459367
460238
  path: c.req.path,
459368
460239
  error: err2
@@ -459380,7 +460251,7 @@ function mountServerLogging(app, initialPort) {
459380
460251
  const fullUrl = `${requestUrl.protocol}//${hostHeader}${reqPath}`;
459381
460252
  const isThreadMessages = reqPath.startsWith("/api/threads/") && reqPath.endsWith("/messages");
459382
460253
  if (!fullUrl.includes("processing") && !reqPath.startsWith("/api/logs") && !isThreadMessages) {
459383
- log159.info(`${method} ${fullUrl}`);
460254
+ log160.info(`${method} ${fullUrl}`);
459384
460255
  }
459385
460256
  return next();
459386
460257
  });
@@ -459422,7 +460293,7 @@ async function selectBuiltInBotThreadIfNeeded(projectManager, threadManager, bui
459422
460293
  async function clearInterruptedTaskWorkingFlags() {
459423
460294
  const clearedWorkingTasks = await clearAllWorkingFlags(await getDatabase());
459424
460295
  if (clearedWorkingTasks > 0) {
459425
- log159.info(
460296
+ log160.info(
459426
460297
  `Cleared working flag from ${clearedWorkingTasks} task(s) interrupted by the previous session`
459427
460298
  );
459428
460299
  }
@@ -459502,6 +460373,7 @@ async function startTarskServerInternal(options2) {
459502
460373
  const { isDebug: isDebug2, publicDir: publicDirOverride } = options2;
459503
460374
  const isRender = process.env.RENDER === "true";
459504
460375
  const initialPort = resolveInitialServerPort(isDebug2);
460376
+ const isDevMode = isTarskDevelopmentMode({ isDebug: isDebug2 });
459505
460377
  const app = new Hono2();
459506
460378
  app.use("/*", cors());
459507
460379
  app.use("/*", async (c, next) => {
@@ -459518,7 +460390,7 @@ async function startTarskServerInternal(options2) {
459518
460390
  const processingStateManager = new ProcessingStateManagerImpl({
459519
460391
  recordConversationProcessingStart(threadId, conversationId, startedAt) {
459520
460392
  void getDatabase().then((db) => recordConversationProcessingStart(db, threadId, conversationId, startedAt)).catch((error2) => {
459521
- log159.warn("Failed to record conversation processing start", {
460393
+ log160.warn("Failed to record conversation processing start", {
459522
460394
  error: error2,
459523
460395
  threadId,
459524
460396
  conversationId
@@ -459527,14 +460399,14 @@ async function startTarskServerInternal(options2) {
459527
460399
  },
459528
460400
  onConversationProcessingCleared(threadId, conversationId) {
459529
460401
  void handleConversationProcessingCleared(threadId, conversationId).catch((error2) => {
459530
- log159.warn("Failed to deliver Cross-project reply", {
460402
+ log160.warn("Failed to deliver Cross-project reply", {
459531
460403
  error: error2,
459532
460404
  threadId,
459533
460405
  conversationId
459534
460406
  });
459535
460407
  });
459536
460408
  void flushBackgroundSubagentWakes(threadId, conversationId).catch(function(error2) {
459537
- log159.warn("Failed to wake parent after background subagent", {
460409
+ log160.warn("Failed to wake parent after background subagent", {
459538
460410
  error: error2,
459539
460411
  threadId,
459540
460412
  conversationId
@@ -459585,6 +460457,9 @@ async function startTarskServerInternal(options2) {
459585
460457
  await initializeLoggingSettings();
459586
460458
  await clearInterruptedTaskWorkingFlags();
459587
460459
  activeTaskSchedulerTimer = startTaskScheduler(taskExecutionDeps);
460460
+ if (process.env.VITEST !== "true") {
460461
+ activePrMergedScannerTimer = startPrMergedScanner({ metadataManager, processingStateManager });
460462
+ }
459588
460463
  registerShutdownSignalHandlers();
459589
460464
  let listeningPort = initialPort;
459590
460465
  registerLocalServerBaseUrl(() => `http://127.0.0.1:${listeningPort}`);
@@ -459593,7 +460468,7 @@ async function startTarskServerInternal(options2) {
459593
460468
  status: "ok",
459594
460469
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
459595
460470
  service: "project-threads-manager-cli",
459596
- isDevMode: listeningPort !== initialPort
460471
+ isDevMode
459597
460472
  });
459598
460473
  });
459599
460474
  app.get("/api/programs", (c) => {
@@ -459723,6 +460598,7 @@ async function startTarskServerInternal(options2) {
459723
460598
  initialPort,
459724
460599
  bunWebSocketSupport,
459725
460600
  injectNodeWebSocket,
460601
+ isDevMode,
459726
460602
  onListening
459727
460603
  });
459728
460604
  registerProcessExitHandler();
@@ -459735,7 +460611,7 @@ function writePortFile(port, url2) {
459735
460611
  fs25.writeFileSync(portFilePath, `${JSON.stringify({ port, url: url2 }, null, 2)}
459736
460612
  `, "utf8");
459737
460613
  } catch (error2) {
459738
- log159.warn("Failed to write port.json", { error: error2 });
460614
+ log160.warn("Failed to write port.json", { error: error2 });
459739
460615
  }
459740
460616
  }
459741
460617
  async function listenForTarskServer(options2) {
@@ -459781,7 +460657,7 @@ async function listenForTarskServer(options2) {
459781
460657
  port += 1;
459782
460658
  }
459783
460659
  }
459784
- var log159, __filename2, __dirname4, SERVER_IDLE_TIMEOUT_SECONDS, startPromise, activeServerStops, activePtySessionManager, activeProcessingStateManager, activeTaskSchedulerTimer, signalHandlersRegistered, PROCESS_EXIT_HANDLER;
460660
+ var log160, __filename2, __dirname4, SERVER_IDLE_TIMEOUT_SECONDS, startPromise, activeServerStops, activePtySessionManager, activeProcessingStateManager, activeTaskSchedulerTimer, activePrMergedScannerTimer, signalHandlersRegistered, PROCESS_EXIT_HANDLER;
459785
460661
  var init_server2 = __esm({
459786
460662
  "src/server.ts"() {
459787
460663
  "use strict";
@@ -459793,6 +460669,7 @@ var init_server2 = __esm({
459793
460669
  init_open();
459794
460670
  init_agent_executor();
459795
460671
  init_agent_processing_state_manager();
460672
+ init_dev_mode();
459796
460673
  init_env_manager();
459797
460674
  init_conversation_processing_state();
459798
460675
  init_paths();
@@ -459846,6 +460723,7 @@ var init_server2 = __esm({
459846
460723
  init_voice_model_routes();
459847
460724
  init_logs_routes();
459848
460725
  init_task_scheduler();
460726
+ init_pr_merged_scanner();
459849
460727
  init_task_execution();
459850
460728
  init_task_launcher();
459851
460729
  init_cross_project_deps();
@@ -459861,7 +460739,7 @@ var init_server2 = __esm({
459861
460739
  init_cli_process_registry();
459862
460740
  init_dev_server_cache();
459863
460741
  init_script_process_manager();
459864
- log159 = createLogger("server");
460742
+ log160 = createLogger("server");
459865
460743
  __filename2 = fileURLToPath7(import.meta.url);
459866
460744
  __dirname4 = path24.dirname(__filename2);
459867
460745
  SERVER_IDLE_TIMEOUT_SECONDS = 255;
@@ -459870,6 +460748,7 @@ var init_server2 = __esm({
459870
460748
  activePtySessionManager = null;
459871
460749
  activeProcessingStateManager = null;
459872
460750
  activeTaskSchedulerTimer = null;
460751
+ activePrMergedScannerTimer = null;
459873
460752
  signalHandlersRegistered = false;
459874
460753
  PROCESS_EXIT_HANDLER = /* @__PURE__ */ Symbol.for("tarsk.server.processExitHandler");
459875
460754
  }