@clinebot/core 0.0.36 → 0.0.37

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 (228) hide show
  1. package/dist/ClineCore.d.ts +312 -3
  2. package/dist/ClineCore.d.ts.map +1 -1
  3. package/dist/account/cline-account-service.d.ts.map +1 -1
  4. package/dist/cron/cron-event-ingress.d.ts +38 -0
  5. package/dist/cron/cron-event-ingress.d.ts.map +1 -0
  6. package/dist/cron/cron-materializer.d.ts +36 -0
  7. package/dist/cron/cron-materializer.d.ts.map +1 -0
  8. package/dist/cron/cron-reconciler.d.ts +62 -0
  9. package/dist/cron/cron-reconciler.d.ts.map +1 -0
  10. package/dist/cron/cron-report-writer.d.ts +41 -0
  11. package/dist/cron/cron-report-writer.d.ts.map +1 -0
  12. package/dist/cron/cron-runner.d.ts +43 -0
  13. package/dist/cron/cron-runner.d.ts.map +1 -0
  14. package/dist/cron/cron-schema.d.ts +3 -0
  15. package/dist/cron/cron-schema.d.ts.map +1 -0
  16. package/dist/cron/cron-service.d.ts +57 -0
  17. package/dist/cron/cron-service.d.ts.map +1 -0
  18. package/dist/cron/cron-spec-parser.d.ts +27 -0
  19. package/dist/cron/cron-spec-parser.d.ts.map +1 -0
  20. package/dist/cron/cron-watcher.d.ts +23 -0
  21. package/dist/cron/cron-watcher.d.ts.map +1 -0
  22. package/dist/cron/scheduler.d.ts +3 -1
  23. package/dist/cron/scheduler.d.ts.map +1 -1
  24. package/dist/cron/sqlite-cron-store.d.ts +230 -0
  25. package/dist/cron/sqlite-cron-store.d.ts.map +1 -0
  26. package/dist/extensions/plugin/plugin-config-loader.d.ts +7 -1
  27. package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
  28. package/dist/extensions/plugin/plugin-loader.d.ts +10 -6
  29. package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
  30. package/dist/extensions/plugin/plugin-sandbox.d.ts +7 -1
  31. package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
  32. package/dist/extensions/plugin-sandbox-bootstrap.js +236 -275
  33. package/dist/extensions/tools/constants.d.ts +1 -0
  34. package/dist/extensions/tools/constants.d.ts.map +1 -1
  35. package/dist/extensions/tools/definitions.d.ts +2 -3
  36. package/dist/extensions/tools/definitions.d.ts.map +1 -1
  37. package/dist/extensions/tools/executors/editor.d.ts.map +1 -1
  38. package/dist/extensions/tools/helpers.d.ts +1 -0
  39. package/dist/extensions/tools/helpers.d.ts.map +1 -1
  40. package/dist/extensions/tools/index.d.ts +1 -2
  41. package/dist/extensions/tools/index.d.ts.map +1 -1
  42. package/dist/extensions/tools/presets.d.ts +1 -1
  43. package/dist/extensions/tools/schemas.d.ts +25 -3
  44. package/dist/extensions/tools/schemas.d.ts.map +1 -1
  45. package/dist/extensions/tools/team/delegated-agent.d.ts +2 -2
  46. package/dist/extensions/tools/team/delegated-agent.d.ts.map +1 -1
  47. package/dist/extensions/tools/team/multi-agent.d.ts +7 -3
  48. package/dist/extensions/tools/team/multi-agent.d.ts.map +1 -1
  49. package/dist/extensions/tools/team/team-tools.d.ts.map +1 -1
  50. package/dist/extensions/tools/types.d.ts +0 -5
  51. package/dist/extensions/tools/types.d.ts.map +1 -1
  52. package/dist/hooks/hook-bridge.d.ts +118 -0
  53. package/dist/hooks/hook-bridge.d.ts.map +1 -0
  54. package/dist/hooks/hook-file-hooks.d.ts +2 -1
  55. package/dist/hooks/hook-file-hooks.d.ts.map +1 -1
  56. package/dist/hooks/hook-registry.d.ts +16 -0
  57. package/dist/hooks/hook-registry.d.ts.map +1 -0
  58. package/dist/hub/browser-websocket.d.ts.map +1 -1
  59. package/dist/hub/client.d.ts +7 -1
  60. package/dist/hub/client.d.ts.map +1 -1
  61. package/dist/hub/daemon-entry.js +721 -461
  62. package/dist/hub/daemon.d.ts.map +1 -1
  63. package/dist/hub/defaults.d.ts +8 -4
  64. package/dist/hub/defaults.d.ts.map +1 -1
  65. package/dist/hub/index.js +665 -415
  66. package/dist/hub/runtime-handlers.d.ts.map +1 -1
  67. package/dist/hub/server.d.ts +18 -0
  68. package/dist/hub/server.d.ts.map +1 -1
  69. package/dist/hub/session-client.d.ts +3 -0
  70. package/dist/hub/session-client.d.ts.map +1 -1
  71. package/dist/hub/start-shared-server.d.ts.map +1 -1
  72. package/dist/hub/ui-client.d.ts +1 -0
  73. package/dist/hub/ui-client.d.ts.map +1 -1
  74. package/dist/index.d.ts +9 -7
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +756 -467
  77. package/dist/llms/cline-recommended-models.d.ts +20 -0
  78. package/dist/llms/cline-recommended-models.d.ts.map +1 -0
  79. package/dist/llms/handler-factory.d.ts +16 -0
  80. package/dist/llms/handler-factory.d.ts.map +1 -0
  81. package/dist/llms/provider-defaults.d.ts.map +1 -1
  82. package/dist/llms/provider-settings.d.ts +45 -2
  83. package/dist/llms/provider-settings.d.ts.map +1 -1
  84. package/dist/llms/runtime-registry.d.ts.map +1 -1
  85. package/dist/runtime/agent-config-adapter.d.ts +148 -0
  86. package/dist/runtime/agent-config-adapter.d.ts.map +1 -0
  87. package/dist/runtime/agent-runtime-config-builder.d.ts +96 -0
  88. package/dist/runtime/agent-runtime-config-builder.d.ts.map +1 -0
  89. package/dist/runtime/history.d.ts +6 -0
  90. package/dist/runtime/history.d.ts.map +1 -1
  91. package/dist/runtime/host.d.ts.map +1 -1
  92. package/dist/runtime/loop-detection.d.ts +59 -0
  93. package/dist/runtime/loop-detection.d.ts.map +1 -0
  94. package/dist/runtime/mistake-tracker.d.ts +69 -0
  95. package/dist/runtime/mistake-tracker.d.ts.map +1 -0
  96. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  97. package/dist/runtime/runtime-event-adapter.d.ts +102 -0
  98. package/dist/runtime/runtime-event-adapter.d.ts.map +1 -0
  99. package/dist/runtime/runtime-host.d.ts +28 -3
  100. package/dist/runtime/runtime-host.d.ts.map +1 -1
  101. package/dist/runtime/session-runtime-orchestrator.d.ts +261 -0
  102. package/dist/runtime/session-runtime-orchestrator.d.ts.map +1 -0
  103. package/dist/runtime/session-runtime.d.ts +16 -3
  104. package/dist/runtime/session-runtime.d.ts.map +1 -1
  105. package/dist/runtime/user-input-builder.d.ts +24 -0
  106. package/dist/runtime/user-input-builder.d.ts.map +1 -0
  107. package/dist/services/index.js +28 -0
  108. package/dist/services/local-runtime-bootstrap.d.ts.map +1 -1
  109. package/dist/services/plugin-tools.d.ts.map +1 -1
  110. package/dist/services/providers/local-provider-registry.d.ts +197 -21
  111. package/dist/services/providers/local-provider-registry.d.ts.map +1 -1
  112. package/dist/services/providers/local-provider-service.d.ts +3 -1
  113. package/dist/services/providers/local-provider-service.d.ts.map +1 -1
  114. package/dist/services/session-data.d.ts.map +1 -1
  115. package/dist/services/session-telemetry.d.ts +7 -2
  116. package/dist/services/session-telemetry.d.ts.map +1 -1
  117. package/dist/services/storage/file-team-store.d.ts.map +1 -1
  118. package/dist/services/storage/provider-settings-legacy-migration.d.ts.map +1 -1
  119. package/dist/services/storage/provider-settings-manager.d.ts +1 -0
  120. package/dist/services/storage/provider-settings-manager.d.ts.map +1 -1
  121. package/dist/services/storage/sqlite-team-store.d.ts.map +1 -1
  122. package/dist/session/conversation-store.d.ts +30 -0
  123. package/dist/session/conversation-store.d.ts.map +1 -0
  124. package/dist/session/message-builder.d.ts +65 -0
  125. package/dist/session/message-builder.d.ts.map +1 -0
  126. package/dist/session/session-manifest.d.ts +1 -1
  127. package/dist/transports/hub.d.ts +14 -3
  128. package/dist/transports/hub.d.ts.map +1 -1
  129. package/dist/transports/local.d.ts +14 -4
  130. package/dist/transports/local.d.ts.map +1 -1
  131. package/dist/transports/remote.d.ts.map +1 -1
  132. package/dist/types/chat-schema.d.ts +5 -5
  133. package/dist/types/config.d.ts +9 -0
  134. package/dist/types/config.d.ts.map +1 -1
  135. package/dist/types/events.d.ts +7 -6
  136. package/dist/types/events.d.ts.map +1 -1
  137. package/dist/types/provider-settings.d.ts +2 -2
  138. package/dist/types/provider-settings.d.ts.map +1 -1
  139. package/dist/types/session.d.ts +5 -2
  140. package/dist/types/session.d.ts.map +1 -1
  141. package/dist/types.d.ts +4 -4
  142. package/dist/types.d.ts.map +1 -1
  143. package/package.json +4 -4
  144. package/src/ClineCore.ts +691 -6
  145. package/src/account/cline-account-service.ts +44 -6
  146. package/src/cron/cron-event-ingress.ts +357 -0
  147. package/src/cron/cron-materializer.ts +97 -0
  148. package/src/cron/cron-reconciler.ts +241 -0
  149. package/src/cron/cron-report-writer.ts +153 -0
  150. package/src/cron/cron-runner.ts +495 -0
  151. package/src/cron/cron-schema.ts +127 -0
  152. package/src/cron/cron-service.ts +163 -0
  153. package/src/cron/cron-spec-parser.ts +489 -0
  154. package/src/cron/cron-watcher.ts +102 -0
  155. package/src/cron/index.ts +10 -0
  156. package/src/cron/scheduler.ts +141 -6
  157. package/src/cron/sqlite-cron-store.ts +1286 -0
  158. package/src/extensions/plugin/plugin-config-loader.ts +21 -1
  159. package/src/extensions/plugin/plugin-loader.ts +25 -9
  160. package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +151 -1
  161. package/src/extensions/plugin/plugin-sandbox.ts +131 -7
  162. package/src/extensions/tools/constants.ts +2 -0
  163. package/src/extensions/tools/definitions.ts +31 -22
  164. package/src/extensions/tools/executors/editor.ts +4 -3
  165. package/src/extensions/tools/helpers.ts +24 -0
  166. package/src/extensions/tools/index.ts +1 -2
  167. package/src/extensions/tools/presets.ts +1 -1
  168. package/src/extensions/tools/schemas.ts +13 -18
  169. package/src/extensions/tools/team/delegated-agent.ts +8 -3
  170. package/src/extensions/tools/team/multi-agent.ts +135 -19
  171. package/src/extensions/tools/team/team-tools.ts +151 -91
  172. package/src/extensions/tools/types.ts +0 -6
  173. package/src/hooks/hook-bridge.ts +489 -0
  174. package/src/hooks/hook-file-hooks.ts +58 -3
  175. package/src/hooks/hook-registry.ts +257 -0
  176. package/src/hub/browser-websocket.ts +26 -4
  177. package/src/hub/client.ts +72 -13
  178. package/src/hub/daemon-entry.ts +35 -0
  179. package/src/hub/daemon.ts +117 -14
  180. package/src/hub/defaults.ts +39 -12
  181. package/src/hub/runtime-handlers.ts +4 -3
  182. package/src/hub/server.ts +506 -77
  183. package/src/hub/session-client.ts +43 -1
  184. package/src/hub/start-shared-server.ts +3 -0
  185. package/src/hub/ui-client.ts +4 -0
  186. package/src/index.ts +46 -1
  187. package/src/llms/cline-recommended-models.ts +167 -0
  188. package/src/llms/handler-factory.ts +56 -0
  189. package/src/llms/provider-defaults.ts +17 -1
  190. package/src/llms/provider-settings.ts +48 -1
  191. package/src/llms/runtime-registry.ts +1 -0
  192. package/src/runtime/agent-config-adapter.ts +636 -0
  193. package/src/runtime/agent-runtime-config-builder.ts +205 -0
  194. package/src/runtime/error-feedback.ts +142 -0
  195. package/src/runtime/history.ts +137 -0
  196. package/src/runtime/host.ts +22 -0
  197. package/src/runtime/loop-detection.ts +162 -0
  198. package/src/runtime/mistake-tracker.ts +221 -0
  199. package/src/runtime/runtime-builder.ts +61 -5
  200. package/src/runtime/runtime-event-adapter.ts +412 -0
  201. package/src/runtime/runtime-host.ts +45 -1
  202. package/src/runtime/session-runtime-orchestrator.ts +1253 -0
  203. package/src/runtime/session-runtime.ts +16 -2
  204. package/src/runtime/user-input-builder.ts +167 -0
  205. package/src/services/local-runtime-bootstrap.ts +128 -22
  206. package/src/services/plugin-tools.ts +1 -0
  207. package/src/services/providers/local-provider-registry.ts +273 -57
  208. package/src/services/providers/local-provider-service.ts +67 -7
  209. package/src/services/session-data.ts +16 -14
  210. package/src/services/session-telemetry.ts +6 -15
  211. package/src/services/storage/file-team-store.ts +1 -5
  212. package/src/services/storage/provider-settings-legacy-migration.ts +8 -47
  213. package/src/services/storage/provider-settings-manager.ts +16 -1
  214. package/src/services/storage/sqlite-team-store.ts +1 -5
  215. package/src/session/conversation-store.ts +77 -0
  216. package/src/session/message-builder.ts +941 -0
  217. package/src/transports/hub.ts +458 -33
  218. package/src/transports/local.ts +296 -65
  219. package/src/transports/remote.ts +1 -0
  220. package/src/types/config.ts +9 -0
  221. package/src/types/events.ts +8 -6
  222. package/src/types/index.ts +3 -0
  223. package/src/types/provider-settings.ts +8 -1
  224. package/src/types/session.ts +5 -2
  225. package/src/types.ts +15 -1
  226. package/dist/cron/index.d.ts +0 -6
  227. package/dist/cron/index.d.ts.map +0 -1
  228. package/dist/services/telemetry/index.js +0 -28
@@ -15,6 +15,7 @@ import {
15
15
  formatReadFileQuery,
16
16
  formatRunCommandQuery,
17
17
  getEditorSizeError,
18
+ getReadFileRangeError,
18
19
  normalizeReadFileRequests,
19
20
  normalizeRunCommandsInput,
20
21
  withTimeout,
@@ -92,6 +93,16 @@ export function createReadFilesTool(
92
93
 
93
94
  return Promise.all(
94
95
  requests.map(async (request): Promise<ToolOperationResult> => {
96
+ const rangeError = getReadFileRangeError(request);
97
+ if (rangeError) {
98
+ return {
99
+ query: formatReadFileQuery(request),
100
+ result: "",
101
+ error: `Invalid file range: ${rangeError}`,
102
+ success: false,
103
+ };
104
+ }
105
+
95
106
  try {
96
107
  const content = await withTimeout(
97
108
  executor(request, context),
@@ -206,16 +217,23 @@ export function createBashTool(
206
217
  maxRetries: 0,
207
218
  execute: async (input, context) => {
208
219
  const validate = validateWithZod(RunCommandsInputUnionSchema, input);
209
- const commands = Array.isArray(validate)
210
- ? validate
211
- : typeof validate === "object"
212
- ? Array.isArray(validate.commands)
213
- ? validate.commands
214
- : [validate.commands]
215
- : [validate];
220
+ let commands: string[];
221
+ if (typeof validate === "string") {
222
+ commands = [validate];
223
+ } else if (Array.isArray(validate)) {
224
+ commands = validate;
225
+ } else if ("commands" in validate) {
226
+ commands = Array.isArray(validate.commands)
227
+ ? validate.commands
228
+ : [validate.commands];
229
+ } else if ("command" in validate) {
230
+ commands = [validate.command];
231
+ } else {
232
+ commands = [validate.cmd];
233
+ }
216
234
 
217
235
  return Promise.all(
218
- commands.map(async (command): Promise<ToolOperationResult> => {
236
+ commands.map(async (command: string): Promise<ToolOperationResult> => {
219
237
  try {
220
238
  const output = await withTimeout(
221
239
  executor(command, cwd, context),
@@ -564,11 +582,8 @@ export function createSkillsTool(
564
582
  */
565
583
  export function createAskQuestionTool(
566
584
  executor: AskQuestionExecutor,
567
- config: Pick<DefaultToolsConfig, "askQuestionTimeoutMs"> = {},
568
585
  ): Tool<AskQuestionInput, string> {
569
- const timeoutMs = config.askQuestionTimeoutMs ?? 15000;
570
-
571
- return createTool<AskQuestionInput, string>({
586
+ return {
572
587
  name: "ask_question",
573
588
  description:
574
589
  "Ask user a question for clarifying or gathering information needed to complete the task. " +
@@ -577,18 +592,13 @@ export function createAskQuestionTool(
577
592
  "Provide an array of 2-5 options for the user to choose from. " +
578
593
  "Never include an option to toggle to Act mode.",
579
594
  inputSchema: zodToJsonSchema(AskQuestionInputSchema),
580
- timeoutMs,
581
595
  retryable: false,
582
596
  maxRetries: 0,
583
597
  execute: async (input, context) => {
584
598
  const validatedInput = validateWithZod(AskQuestionInputSchema, input);
585
- return withTimeout(
586
- executor(validatedInput.question, validatedInput.options, context),
587
- timeoutMs,
588
- `ask_question timed out after ${timeoutMs}ms`,
589
- );
599
+ return executor(validatedInput.question, validatedInput.options, context);
590
600
  },
591
- });
601
+ };
592
602
  }
593
603
 
594
604
  export function createSubmitAndExitTool(
@@ -632,8 +642,7 @@ export function createSubmitAndExitTool(
632
642
  *
633
643
  * @example
634
644
  * ```typescript
635
- * import { Agent } from "@clinebot/agents"
636
- * import { createDefaultTools } from "@clinebot/core"
645
+ * import { Agent, createDefaultTools } from "@clinebot/core"
637
646
  * import * as fs from "fs/promises"
638
647
  * import { exec } from "child_process"
639
648
  *
@@ -719,7 +728,7 @@ export function createDefaultTools(options: CreateDefaultToolsOptions): Tool[] {
719
728
 
720
729
  // Add ask_question tool if enabled and executor provided
721
730
  if (enableAskQuestion && executors.askQuestion) {
722
- tools.push(createAskQuestionTool(executors.askQuestion, config));
731
+ tools.push(createAskQuestionTool(executors.askQuestion));
723
732
  } else if (enableSubmitAndExit && executors.submit) {
724
733
  // Add submit_and_exit tool if enabled and executor provided
725
734
  tools.push(createSubmitAndExitTool(executors.submit, config));
@@ -157,14 +157,15 @@ async function insertInFile(
157
157
  ): Promise<string> {
158
158
  const content = await fs.readFile(filePath, encoding);
159
159
  const lines = content.split("\n");
160
- const insertLine = insertLineOneBased - 1; // Convert to zero-based index
160
+ const maxBoundaryLine = lines.length + 1;
161
161
 
162
- if (insertLine < 0 || insertLine > lines.length) {
162
+ if (insertLineOneBased < 1 || insertLineOneBased > maxBoundaryLine) {
163
163
  throw new Error(
164
- `Invalid line number: ${insertLineOneBased}. Valid range: 1-${lines.length}`,
164
+ `Invalid insert_line: ${insertLineOneBased}. insert_line must be a positive one-based boundary line in the range 1-${maxBoundaryLine}. Use ${maxBoundaryLine} to append at EOF.`,
165
165
  );
166
166
  }
167
167
 
168
+ const insertLine = insertLineOneBased - 1;
168
169
  lines.splice(insertLine, 0, ...newStr.split("\n"));
169
170
  await fs.writeFile(filePath, lines.join("\n"), { encoding });
170
171
 
@@ -76,6 +76,13 @@ export function normalizeReadFileRequests(input: unknown): ReadFileRequest[] {
76
76
  return filePaths.map((filePath) => ({ path: filePath }));
77
77
  }
78
78
 
79
+ if ("paths" in validate) {
80
+ const paths = Array.isArray(validate.paths)
81
+ ? validate.paths
82
+ : [validate.paths];
83
+ return paths.map((path) => (typeof path === "string" ? { path } : path));
84
+ }
85
+
79
86
  return [validate];
80
87
  }
81
88
 
@@ -89,6 +96,15 @@ export function formatReadFileQuery(request: ReadFileRequest): string {
89
96
  return `${path}:${start}-${end}`;
90
97
  }
91
98
 
99
+ export function getReadFileRangeError(request: ReadFileRequest): string | null {
100
+ const { start_line, end_line } = request;
101
+ if (start_line == null || end_line == null || start_line <= end_line) {
102
+ return null;
103
+ }
104
+
105
+ return `start_line must be less than or equal to end_line (received start_line: ${start_line}, end_line: ${end_line})`;
106
+ }
107
+
92
108
  export function normalizeRunCommandsInput(
93
109
  input: unknown,
94
110
  ): Array<string | StructuredCommandInput> {
@@ -108,6 +124,14 @@ export function normalizeRunCommandsInput(
108
124
  : [validate.commands];
109
125
  }
110
126
 
127
+ if ("command" in validate) {
128
+ return "args" in validate ? [validate] : [validate.command];
129
+ }
130
+
131
+ if ("cmd" in validate) {
132
+ return [validate.cmd];
133
+ }
134
+
111
135
  return [validate];
112
136
  }
113
137
 
@@ -143,8 +143,7 @@ export interface CreateBuiltinToolsOptions
143
143
  *
144
144
  * @example
145
145
  * ```typescript
146
- * import { Agent } from "@clinebot/agents"
147
- * import { createBuiltinTools } from "@clinebot/core"
146
+ * import { Agent, createBuiltinTools } from "@clinebot/core"
148
147
  *
149
148
  * const tools = createBuiltinTools({
150
149
  * cwd: "/path/to/project",
@@ -26,7 +26,7 @@ export const ToolPresets = {
26
26
  enableReadFiles: true,
27
27
  enableSearch: true,
28
28
  enableBash: true,
29
- enableWebFetch: false,
29
+ enableWebFetch: true,
30
30
  enableApplyPatch: false,
31
31
  enableEditor: true,
32
32
  enableSkills: true,
@@ -37,14 +37,7 @@ export const ReadFileLineRangeSchema = z
37
37
  "Optional one-based ending line number to read through; use null or omit for the end of the file",
38
38
  ),
39
39
  })
40
- .refine(
41
- ({ start_line, end_line }) =>
42
- start_line == null || end_line == null || start_line <= end_line,
43
- {
44
- message: "start_line must be less than or equal to end_line",
45
- path: ["end_line"],
46
- },
47
- );
40
+ .describe("Optional inclusive one-based file line range");
48
41
 
49
42
  export const ReadFileRequestSchema = z
50
43
  .object({
@@ -52,13 +45,8 @@ export const ReadFileRequestSchema = z
52
45
  start_line: ReadFileLineRangeSchema.shape.start_line,
53
46
  end_line: ReadFileLineRangeSchema.shape.end_line,
54
47
  })
55
- .refine(
56
- ({ start_line, end_line }) =>
57
- start_line == null || end_line == null || start_line <= end_line,
58
- {
59
- message: "start_line must be less than or equal to end_line",
60
- path: ["end_line"],
61
- },
48
+ .describe(
49
+ "A file read request with optional inclusive one-based line bounds",
62
50
  );
63
51
 
64
52
  /**
@@ -84,6 +72,9 @@ export const ReadFilesInputUnionSchema = z.union([
84
72
  z.object({ files: ReadFileRequestSchema }),
85
73
  z.object({ file_paths: z.array(AbsolutePath) }),
86
74
  z.object({ file_paths: z.string() }),
75
+ z.object({ paths: z.array(z.union([AbsolutePath, ReadFileRequestSchema])) }),
76
+ z.object({ paths: ReadFileRequestSchema }),
77
+ z.object({ paths: z.string() }),
87
78
  ]);
88
79
 
89
80
  /**
@@ -126,6 +117,8 @@ export const RunCommandsInputSchema = z.object({
126
117
  export const RunCommandsInputUnionSchema = z.union([
127
118
  RunCommandsInputSchema,
128
119
  z.object({ commands: CommandInputSchema }),
120
+ z.object({ command: CommandInputSchema }),
121
+ z.object({ cmd: CommandInputSchema }),
129
122
  z.array(z.string()),
130
123
  z.string(),
131
124
  ]);
@@ -163,10 +156,12 @@ export const StructuredCommandsInputUnionSchema = z.union([
163
156
  RunCommandsInputSchema,
164
157
  StructuredCommandsInputSchema,
165
158
  z.object({ commands: StructuredCommandEntrySchema }),
166
- z.array(z.string()),
167
159
  z.array(StructuredCommandInputSchema),
168
- z.string(),
169
160
  StructuredCommandInputSchema,
161
+ z.object({ command: CommandInputSchema }),
162
+ z.object({ cmd: CommandInputSchema }),
163
+ z.array(z.string()),
164
+ z.string(),
170
165
  ]);
171
166
 
172
167
  /**
@@ -213,7 +208,7 @@ export const EditFileInputSchema = z
213
208
  .nullable()
214
209
  .optional()
215
210
  .describe(
216
- "Optional one-based line index. When provided, the tool inserts new_text at that line instead of performing a replacement edit.",
211
+ "Optional positive one-based boundary line. When provided, the tool inserts new_text before that line instead of performing a replacement edit; use line_count + 1 to append at EOF.",
217
212
  ),
218
213
  })
219
214
  .describe(
@@ -1,4 +1,3 @@
1
- import { Agent } from "@clinebot/agents";
2
1
  import type {
3
2
  AgentConfig,
4
3
  AgentEvent,
@@ -10,6 +9,7 @@ import type {
10
9
  ToolApprovalRequest,
11
10
  ToolApprovalResult,
12
11
  } from "@clinebot/shared";
12
+ import { SessionRuntime } from "../../../runtime/session-runtime-orchestrator";
13
13
  import {
14
14
  buildSubAgentSystemPrompt,
15
15
  buildTeammateSystemPrompt,
@@ -126,6 +126,11 @@ export function buildDelegatedAgentConfig(
126
126
 
127
127
  export function createDelegatedAgent(
128
128
  options: BuildDelegatedAgentConfigOptions,
129
- ): Agent {
130
- return new Agent(buildDelegatedAgentConfig(options));
129
+ ): SessionRuntime {
130
+ const config = buildDelegatedAgentConfig(options);
131
+ const session = new SessionRuntime(config);
132
+ if (config.onEvent) {
133
+ session.subscribeEvents(config.onEvent);
134
+ }
135
+ return session;
131
136
  }
@@ -4,7 +4,6 @@
4
4
  * Utilities for orchestrating multiple agents working together.
5
5
  */
6
6
 
7
- import { type Agent, createAgent } from "@clinebot/agents";
8
7
  import {
9
8
  type AgentConfig,
10
9
  type AgentEvent,
@@ -33,6 +32,7 @@ import {
33
32
  type TeamTaskStatus,
34
33
  } from "@clinebot/shared";
35
34
  import { nanoid } from "nanoid";
35
+ import { SessionRuntime } from "../../../runtime/session-runtime-orchestrator";
36
36
 
37
37
  // Re-export shared types for backward compatibility
38
38
  export {
@@ -155,14 +155,26 @@ function isAbortLikeError(error: unknown): boolean {
155
155
  );
156
156
  }
157
157
 
158
+ function isIntentionalShutdownAbort(
159
+ member: TeamMemberState | undefined,
160
+ error: unknown,
161
+ ): boolean {
162
+ return member?.status === "stopped" && isAbortLikeError(error);
163
+ }
164
+
158
165
  // =============================================================================
159
166
  // AgentTeam
160
167
  // =============================================================================
161
168
 
162
169
  const TEAMMATE_API_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
170
+ const RECOVERED_QUEUED_ACTIVITY = "recovered_queued";
171
+
172
+ function buildRecoveredRunMessage(run: TeamRunRecord): string {
173
+ return `This is an automatic recovery of interrupted team run ${run.id}. The previous process stopped before completion. Continue the task safely, inspect the current workspace state before making changes, and avoid duplicating completed work.\n\n${run.message}`;
174
+ }
163
175
 
164
176
  export class AgentTeam {
165
- private agents: Map<string, Agent> = new Map();
177
+ private agents: Map<string, SessionRuntime> = new Map();
166
178
  private configs: Map<string, TeamMemberConfig> = new Map();
167
179
  private onTeamEvent?: (event: TeamEvent) => void;
168
180
 
@@ -196,7 +208,10 @@ export class AgentTeam {
196
208
  },
197
209
  };
198
210
 
199
- const agent = createAgent(wrappedConfig);
211
+ const agent = new SessionRuntime(wrappedConfig);
212
+ if (wrappedConfig.onEvent) {
213
+ agent.subscribeEvents(wrappedConfig.onEvent);
214
+ }
200
215
  this.agents.set(id, agent);
201
216
  this.configs.set(id, config);
202
217
  }
@@ -206,7 +221,7 @@ export class AgentTeam {
206
221
  return this.agents.delete(id);
207
222
  }
208
223
 
209
- getAgent(id: string): Agent | undefined {
224
+ getAgent(id: string): SessionRuntime | undefined {
210
225
  return this.agents.get(id);
211
226
  }
212
227
 
@@ -497,7 +512,7 @@ export function createWorkerReviewerTeam(configs: {
497
512
  // =============================================================================
498
513
 
499
514
  interface TeamMemberState extends TeamMemberSnapshot {
500
- agent?: Agent;
515
+ agent?: SessionRuntime;
501
516
  runningCount: number;
502
517
  lastMissionStep: number;
503
518
  lastMissionAt: number;
@@ -522,6 +537,7 @@ export class AgentTeamsRuntime {
522
537
  private readonly runs: Map<string, TeamRunRecord & { result?: AgentResult }> =
523
538
  new Map();
524
539
  private readonly runQueue: string[] = [];
540
+ private queuedRunDispatchTimer: ReturnType<typeof setTimeout> | undefined;
525
541
  private readonly outcomes: Map<string, TeamOutcome> = new Map();
526
542
  private readonly outcomeFragments: Map<string, TeamOutcomeFragment> =
527
543
  new Map();
@@ -707,6 +723,7 @@ export class AgentTeamsRuntime {
707
723
  }
708
724
 
709
725
  hydrateState(state: TeamRuntimeState): void {
726
+ this.clearQueuedRunDispatchTimer();
710
727
  this.tasks.clear();
711
728
  for (const task of state.tasks) {
712
729
  this.tasks.set(task.id, { ...task });
@@ -851,7 +868,10 @@ export class AgentTeamsRuntime {
851
868
  },
852
869
  };
853
870
 
854
- const agent = createAgent(wrappedConfig);
871
+ const agent = new SessionRuntime(wrappedConfig);
872
+ if (wrappedConfig.onEvent) {
873
+ agent.subscribeEvents(wrappedConfig.onEvent);
874
+ }
855
875
  const teammate: TeamMemberState = {
856
876
  agentId,
857
877
  role: "teammate",
@@ -1038,12 +1058,14 @@ export class AgentTeamsRuntime {
1038
1058
  error: err,
1039
1059
  messages: member.agent.getMessages(),
1040
1060
  });
1041
- this.appendMissionLog({
1042
- agentId,
1043
- taskId: options?.taskId,
1044
- kind: "error",
1045
- summary: err.message,
1046
- });
1061
+ if (!isIntentionalShutdownAbort(member, err)) {
1062
+ this.appendMissionLog({
1063
+ agentId,
1064
+ taskId: options?.taskId,
1065
+ kind: "error",
1066
+ summary: err.message,
1067
+ });
1068
+ }
1047
1069
  throw err;
1048
1070
  } finally {
1049
1071
  member.runningCount--;
@@ -1091,11 +1113,19 @@ export class AgentTeamsRuntime {
1091
1113
  }
1092
1114
 
1093
1115
  private dispatchQueuedRuns(): void {
1116
+ this.clearQueuedRunDispatchTimer();
1117
+ let nextDelayedAttemptAt: Date | undefined;
1094
1118
  while (
1095
1119
  this.countActiveRuns() < this.maxConcurrentRuns &&
1096
1120
  this.runQueue.length > 0
1097
1121
  ) {
1098
- const nextRunIndex = this.selectNextQueuedRunIndex();
1122
+ const nextRun = this.selectNextDispatchableQueuedRun();
1123
+ nextDelayedAttemptAt = nextRun.nextDelayedAttemptAt;
1124
+ const nextRunIndex = nextRun.index;
1125
+ if (nextRunIndex < 0) {
1126
+ this.scheduleQueuedRunDispatch(nextDelayedAttemptAt);
1127
+ return;
1128
+ }
1099
1129
  const [runId] = this.runQueue.splice(nextRunIndex, 1);
1100
1130
  const run = runId ? this.runs.get(runId) : undefined;
1101
1131
  if (!run || run.status !== "queued") {
@@ -1103,22 +1133,53 @@ export class AgentTeamsRuntime {
1103
1133
  }
1104
1134
  void this.executeQueuedRun(run);
1105
1135
  }
1136
+ this.scheduleQueuedRunDispatch(nextDelayedAttemptAt);
1106
1137
  }
1107
1138
 
1108
- private selectNextQueuedRunIndex(): number {
1109
- let selectedIndex = 0;
1139
+ private selectNextDispatchableQueuedRun(): {
1140
+ index: number;
1141
+ nextDelayedAttemptAt?: Date;
1142
+ } {
1143
+ let selectedIndex = -1;
1110
1144
  let bestPriority = Number.NEGATIVE_INFINITY;
1145
+ let nextDelayedAttemptAt: Date | undefined;
1146
+ const now = Date.now();
1111
1147
  for (let index = 0; index < this.runQueue.length; index++) {
1112
1148
  const run = this.runs.get(this.runQueue[index]);
1113
1149
  if (!run || run.status !== "queued") {
1114
1150
  continue;
1115
1151
  }
1152
+ if (run.nextAttemptAt && run.nextAttemptAt.getTime() > now) {
1153
+ if (!nextDelayedAttemptAt || run.nextAttemptAt < nextDelayedAttemptAt) {
1154
+ nextDelayedAttemptAt = run.nextAttemptAt;
1155
+ }
1156
+ continue;
1157
+ }
1116
1158
  if (run.priority > bestPriority) {
1117
1159
  bestPriority = run.priority;
1118
1160
  selectedIndex = index;
1119
1161
  }
1120
1162
  }
1121
- return selectedIndex;
1163
+ return { index: selectedIndex, nextDelayedAttemptAt };
1164
+ }
1165
+
1166
+ private scheduleQueuedRunDispatch(nextAttemptAt: Date | undefined): void {
1167
+ if (!nextAttemptAt) {
1168
+ return;
1169
+ }
1170
+ const delayMs = Math.max(0, nextAttemptAt.getTime() - Date.now());
1171
+ this.queuedRunDispatchTimer = setTimeout(() => {
1172
+ this.queuedRunDispatchTimer = undefined;
1173
+ this.dispatchQueuedRuns();
1174
+ }, delayMs);
1175
+ }
1176
+
1177
+ private clearQueuedRunDispatchTimer(): void {
1178
+ if (!this.queuedRunDispatchTimer) {
1179
+ return;
1180
+ }
1181
+ clearTimeout(this.queuedRunDispatchTimer);
1182
+ this.queuedRunDispatchTimer = undefined;
1122
1183
  }
1123
1184
 
1124
1185
  private countActiveRuns(): number {
@@ -1134,6 +1195,8 @@ export class AgentTeamsRuntime {
1134
1195
  private async executeQueuedRun(
1135
1196
  run: TeamRunRecord & { result?: AgentResult },
1136
1197
  ): Promise<void> {
1198
+ const recoveredRun = run.currentActivity === RECOVERED_QUEUED_ACTIVITY;
1199
+ run.nextAttemptAt = undefined;
1137
1200
  run.status = "running";
1138
1201
  run.startedAt = new Date();
1139
1202
  run.heartbeatAt = new Date();
@@ -1148,7 +1211,10 @@ export class AgentTeamsRuntime {
1148
1211
  }, 2000);
1149
1212
 
1150
1213
  try {
1151
- const result = await this.routeToTeammate(run.agentId, run.message, {
1214
+ const runMessage = recoveredRun
1215
+ ? buildRecoveredRunMessage(run)
1216
+ : run.message;
1217
+ const result = await this.routeToTeammate(run.agentId, runMessage, {
1152
1218
  taskId: run.taskId,
1153
1219
  continueConversation: run.continueConversation,
1154
1220
  });
@@ -1164,7 +1230,16 @@ export class AgentTeamsRuntime {
1164
1230
  : String(error ?? "Unknown error");
1165
1231
  run.error = message;
1166
1232
  run.endedAt = new Date();
1167
- if (run.retryCount < run.maxRetries) {
1233
+ const member = this.members.get(run.agentId);
1234
+ if (isIntentionalShutdownAbort(member, error)) {
1235
+ run.status = "cancelled";
1236
+ run.currentActivity = "cancelled";
1237
+ this.emitEvent({
1238
+ type: TeamMessageType.RunCancelled,
1239
+ run: { ...run },
1240
+ reason: message,
1241
+ });
1242
+ } else if (run.retryCount < run.maxRetries) {
1168
1243
  run.retryCount++;
1169
1244
  run.status = "queued";
1170
1245
  run.nextAttemptAt = new Date(
@@ -1215,7 +1290,7 @@ export class AgentTeamsRuntime {
1215
1290
  if (!run) {
1216
1291
  throw new Error(`Run "${runId}" was not found`);
1217
1292
  }
1218
- while (run.status === "running") {
1293
+ while (run.status === "queued" || run.status === "running") {
1219
1294
  await sleep(pollIntervalMs);
1220
1295
  }
1221
1296
  return { ...run };
@@ -1256,6 +1331,45 @@ export class AgentTeamsRuntime {
1256
1331
  return { ...run };
1257
1332
  }
1258
1333
 
1334
+ recoverActiveRuns(reason = "runtime_recovered"): TeamRunRecord[] {
1335
+ const recovered: TeamRunRecord[] = [];
1336
+ for (const run of this.runs.values()) {
1337
+ if (!["queued", "running"].includes(run.status)) {
1338
+ continue;
1339
+ }
1340
+
1341
+ const member = this.members.get(run.agentId);
1342
+ if (!member || member.role !== "teammate" || !member.agent) {
1343
+ run.status = "interrupted";
1344
+ run.error = "teammate_unavailable_after_recovery";
1345
+ run.endedAt = new Date();
1346
+ run.currentActivity = "interrupted";
1347
+ this.emitEvent({
1348
+ type: TeamMessageType.RunInterrupted,
1349
+ run: { ...run },
1350
+ reason: run.error,
1351
+ });
1352
+ continue;
1353
+ }
1354
+
1355
+ const now = new Date();
1356
+ run.status = "queued";
1357
+ run.error = undefined;
1358
+ run.endedAt = undefined;
1359
+ run.heartbeatAt = now;
1360
+ run.lastProgressAt = now;
1361
+ run.lastProgressMessage = reason;
1362
+ run.currentActivity = RECOVERED_QUEUED_ACTIVITY;
1363
+ if (!this.runQueue.includes(run.id)) {
1364
+ this.runQueue.push(run.id);
1365
+ }
1366
+ recovered.push({ ...run });
1367
+ this.emitEvent({ type: TeamMessageType.RunQueued, run: { ...run } });
1368
+ }
1369
+ this.dispatchQueuedRuns();
1370
+ return recovered;
1371
+ }
1372
+
1259
1373
  markStaleRunsInterrupted(reason = "runtime_recovered"): TeamRunRecord[] {
1260
1374
  const interrupted: TeamRunRecord[] = [];
1261
1375
  for (const run of this.runs.values()) {
@@ -1274,6 +1388,7 @@ export class AgentTeamsRuntime {
1274
1388
  });
1275
1389
  }
1276
1390
  this.runQueue.length = 0;
1391
+ this.clearQueuedRunDispatchTimer();
1277
1392
  return interrupted;
1278
1393
  }
1279
1394
 
@@ -1513,6 +1628,7 @@ export class AgentTeamsRuntime {
1513
1628
  this.missionLog.length = 0;
1514
1629
  this.runs.clear();
1515
1630
  this.runQueue.length = 0;
1631
+ this.clearQueuedRunDispatchTimer();
1516
1632
  this.outcomes.clear();
1517
1633
  this.outcomeFragments.clear();
1518
1634