tarsk 0.3.41 → 0.3.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Hono as Hono11 } from "hono";
4
+ import { Hono as Hono12 } from "hono";
5
5
  import { cors } from "hono/cors";
6
6
  import { serve } from "@hono/node-server";
7
7
  import { serveStatic } from "@hono/node-server/serve-static";
8
8
  import path3 from "path";
9
+ import fs2 from "fs";
9
10
  import open3 from "open";
10
11
  import { fileURLToPath as fileURLToPath2 } from "url";
11
12
 
@@ -4491,13 +4492,77 @@ async function listReferencesRecursive(dir, prefix = "") {
4491
4492
  }
4492
4493
  return files;
4493
4494
  }
4495
+
4496
+ // src/tools/ask-user.ts
4497
+ import { Type as Type10 } from "@sinclair/typebox";
4498
+ var askUserSchema = Type10.Object({
4499
+ question: Type10.String({
4500
+ description: "A clear, specific question to ask the user. Frame it to help clarify ambiguous requirements, confirm decisions, or gather preferences needed to proceed."
4501
+ }),
4502
+ options: Type10.Optional(Type10.Array(Type10.String(), {
4503
+ description: "Optional list of predefined answer choices. The user can pick one or type a custom response. Use when there are a small number of clear alternatives."
4504
+ })),
4505
+ context: Type10.Optional(Type10.String({
4506
+ description: "Optional brief context explaining why this question is being asked, so the user understands the impact of their answer."
4507
+ }))
4508
+ });
4509
+ var pendingQuestions = new Map;
4510
+ function submitAnswer(toolCallId, answer) {
4511
+ const pending = pendingQuestions.get(toolCallId);
4512
+ if (!pending)
4513
+ return false;
4514
+ pending.resolve(answer);
4515
+ pendingQuestions.delete(toolCallId);
4516
+ return true;
4517
+ }
4518
+ function cancelPendingQuestion(toolCallId) {
4519
+ const pending = pendingQuestions.get(toolCallId);
4520
+ if (!pending)
4521
+ return false;
4522
+ pending.reject(new Error("Question cancelled"));
4523
+ pendingQuestions.delete(toolCallId);
4524
+ return true;
4525
+ }
4526
+ function createAskUserTool() {
4527
+ return {
4528
+ name: "ask_user",
4529
+ label: "ask_user",
4530
+ description: "Ask the user a question when you need clarification, confirmation, or a decision when there is ambiguity or multiple choices.",
4531
+ parameters: askUserSchema,
4532
+ execute: async (toolCallId, { question, options, context }, signal) => {
4533
+ console.log(`[ai] ask_user: "${question}" (options: ${options?.join(", ") ?? "free-text"})`);
4534
+ if (signal?.aborted) {
4535
+ throw new Error("Operation aborted");
4536
+ }
4537
+ const answer = await new Promise((resolve, reject) => {
4538
+ pendingQuestions.set(toolCallId, {
4539
+ resolve,
4540
+ reject,
4541
+ question,
4542
+ options,
4543
+ context
4544
+ });
4545
+ const onAbort = () => {
4546
+ cancelPendingQuestion(toolCallId);
4547
+ };
4548
+ signal?.addEventListener("abort", onAbort, { once: true });
4549
+ });
4550
+ console.log(`[ai] ask_user response: "${answer}"`);
4551
+ return {
4552
+ content: [{ type: "text", text: answer }]
4553
+ };
4554
+ }
4555
+ };
4556
+ }
4557
+ var askUserTool = createAskUserTool();
4494
4558
  // src/tools/index.ts
4495
4559
  function createCodingTools(cwd, options) {
4496
4560
  const baseTools = [
4497
4561
  createReadTool(cwd, options?.read),
4498
4562
  createBashTool(cwd, options?.bash),
4499
4563
  createEditTool(cwd),
4500
- createWriteTool(cwd)
4564
+ createWriteTool(cwd),
4565
+ createAskUserTool()
4501
4566
  ];
4502
4567
  if (options?.skills && options.skills.length > 0) {
4503
4568
  const skillTools = [
@@ -4513,6 +4578,7 @@ function createCodingTools(cwd, options) {
4513
4578
  var PROVIDERS = [
4514
4579
  {
4515
4580
  name: "AIHubMix",
4581
+ slug: "aihubmix",
4516
4582
  url: "https://aihubmix.com/",
4517
4583
  keyName: "AIHUBMIX_API_KEY",
4518
4584
  authType: "APIKey",
@@ -4520,6 +4586,7 @@ var PROVIDERS = [
4520
4586
  },
4521
4587
  {
4522
4588
  name: "Anthropic",
4589
+ slug: "anthropic",
4523
4590
  url: "https://console.anthropic.com/settings/keys",
4524
4591
  keyName: "ANTHROPIC_API_KEY",
4525
4592
  authType: "APIKey",
@@ -4527,6 +4594,7 @@ var PROVIDERS = [
4527
4594
  },
4528
4595
  {
4529
4596
  name: "CanopyWave",
4597
+ slug: "canopywave",
4530
4598
  url: "https://cloud.canopywave.io/",
4531
4599
  keyName: "CANOPYWAVE_API_KEY",
4532
4600
  authType: "APIKey",
@@ -4534,6 +4602,7 @@ var PROVIDERS = [
4534
4602
  },
4535
4603
  {
4536
4604
  name: "Codex",
4605
+ slug: "codex",
4537
4606
  url: "",
4538
4607
  keyName: "",
4539
4608
  authType: "OAuth",
@@ -4541,6 +4610,7 @@ var PROVIDERS = [
4541
4610
  },
4542
4611
  {
4543
4612
  name: "Cerebras",
4613
+ slug: "cerebras",
4544
4614
  url: "https://cerebras.ai/",
4545
4615
  keyName: "CEREBRAS_API_KEY",
4546
4616
  authType: "APIKey",
@@ -4548,6 +4618,7 @@ var PROVIDERS = [
4548
4618
  },
4549
4619
  {
4550
4620
  name: "DeepSeek",
4621
+ slug: "deepseek",
4551
4622
  url: "https://platform.deepseek.com/",
4552
4623
  keyName: "DEEPSEEK_API_KEY",
4553
4624
  authType: "APIKey",
@@ -4555,6 +4626,7 @@ var PROVIDERS = [
4555
4626
  },
4556
4627
  {
4557
4628
  name: "GitHub Copilot",
4629
+ slug: "github-copilot",
4558
4630
  url: "",
4559
4631
  keyName: "",
4560
4632
  authType: "OAuth",
@@ -4562,6 +4634,7 @@ var PROVIDERS = [
4562
4634
  },
4563
4635
  {
4564
4636
  name: "Google",
4637
+ slug: "google",
4565
4638
  url: "https://aistudio.google.com/apikey",
4566
4639
  keyName: "GOOGLE_API_KEY",
4567
4640
  authType: "APIKey",
@@ -4569,6 +4642,7 @@ var PROVIDERS = [
4569
4642
  },
4570
4643
  {
4571
4644
  name: "Groq",
4645
+ slug: "groq",
4572
4646
  url: "https://console.groq.com/",
4573
4647
  keyName: "GROQ_API_KEY",
4574
4648
  authType: "APIKey",
@@ -4576,6 +4650,7 @@ var PROVIDERS = [
4576
4650
  },
4577
4651
  {
4578
4652
  name: "Hugging Face",
4653
+ slug: "hugging-face",
4579
4654
  url: "https://huggingface.co/settings/tokens",
4580
4655
  keyName: "HUGGINGFACE_API_KEY",
4581
4656
  authType: "APIKey",
@@ -4583,6 +4658,7 @@ var PROVIDERS = [
4583
4658
  },
4584
4659
  {
4585
4660
  name: "iFlow",
4661
+ slug: "iflow",
4586
4662
  url: "https://iflow.cn/",
4587
4663
  keyName: "IFLOW_API_KEY",
4588
4664
  authType: "APIKey",
@@ -4590,6 +4666,7 @@ var PROVIDERS = [
4590
4666
  },
4591
4667
  {
4592
4668
  name: "Minimax",
4669
+ slug: "minimax",
4593
4670
  url: "https://platform.minimaxi.com/",
4594
4671
  keyName: "MINIMAX_API_KEY",
4595
4672
  authType: "APIKey",
@@ -4597,6 +4674,7 @@ var PROVIDERS = [
4597
4674
  },
4598
4675
  {
4599
4676
  name: "Kimi Coding Plan",
4677
+ slug: "kimi",
4600
4678
  url: "https://www.kimi.com/code/docs/more/third-party-agents.html",
4601
4679
  keyName: "KIMI_CODING_API_KEY",
4602
4680
  authType: "APIKey",
@@ -4604,6 +4682,7 @@ var PROVIDERS = [
4604
4682
  },
4605
4683
  {
4606
4684
  name: "ModelScope",
4685
+ slug: "modelscope",
4607
4686
  url: "https://console.modelscope.cn/",
4608
4687
  keyName: "MODELSCOPE_API_KEY",
4609
4688
  authType: "APIKey",
@@ -4611,6 +4690,7 @@ var PROVIDERS = [
4611
4690
  },
4612
4691
  {
4613
4692
  name: "ModelWatch",
4693
+ slug: "modelwatch",
4614
4694
  url: "https://api.modelwatch.dev/",
4615
4695
  keyName: "MODELWATCH_API_KEY",
4616
4696
  authType: "APIKey",
@@ -4618,6 +4698,7 @@ var PROVIDERS = [
4618
4698
  },
4619
4699
  {
4620
4700
  name: "Moonshot AI",
4701
+ slug: "moonshot-ai",
4621
4702
  url: "https://platform.moonshot.ai/console",
4622
4703
  keyName: "MOONSHOT_API_KEY",
4623
4704
  authType: "APIKey",
@@ -4625,6 +4706,7 @@ var PROVIDERS = [
4625
4706
  },
4626
4707
  {
4627
4708
  name: "NVIDIA",
4709
+ slug: "nvidia",
4628
4710
  url: "https://build.nvidia.com/models",
4629
4711
  keyName: "NVIDIA_API_KEY",
4630
4712
  authType: "APIKey",
@@ -4632,6 +4714,7 @@ var PROVIDERS = [
4632
4714
  },
4633
4715
  {
4634
4716
  name: "OpenAI",
4717
+ slug: "openai",
4635
4718
  url: "https://platform.openai.com/api-keys",
4636
4719
  keyName: "OPENAI_API_KEY",
4637
4720
  authType: "APIKey",
@@ -4639,6 +4722,7 @@ var PROVIDERS = [
4639
4722
  },
4640
4723
  {
4641
4724
  name: "OpenRouter",
4725
+ slug: "openrouter",
4642
4726
  url: "https://openrouter.ai/settings/keys",
4643
4727
  keyName: "OPENROUTER_API_KEY",
4644
4728
  authType: "APIKey",
@@ -4646,6 +4730,7 @@ var PROVIDERS = [
4646
4730
  },
4647
4731
  {
4648
4732
  name: "Poe",
4733
+ slug: "poe",
4649
4734
  url: "https://poe.com/",
4650
4735
  keyName: "POE_API_KEY",
4651
4736
  authType: "APIKey",
@@ -4653,6 +4738,7 @@ var PROVIDERS = [
4653
4738
  },
4654
4739
  {
4655
4740
  name: "SiliconFlow",
4741
+ slug: "siliconflow",
4656
4742
  url: "https://siliconflow.com/",
4657
4743
  keyName: "SILICONFLOW_API_KEY",
4658
4744
  authType: "APIKey",
@@ -4660,6 +4746,7 @@ var PROVIDERS = [
4660
4746
  },
4661
4747
  {
4662
4748
  name: "VolcEngine",
4749
+ slug: "volcengine",
4663
4750
  url: "https://www.volcengine.com/",
4664
4751
  keyName: "VOLCENGINE_API_KEY",
4665
4752
  authType: "APIKey",
@@ -4667,6 +4754,7 @@ var PROVIDERS = [
4667
4754
  },
4668
4755
  {
4669
4756
  name: "xAI",
4757
+ slug: "xai",
4670
4758
  url: "https://console.x.ai/",
4671
4759
  keyName: "XAI_API_KEY",
4672
4760
  authType: "APIKey",
@@ -4674,6 +4762,7 @@ var PROVIDERS = [
4674
4762
  },
4675
4763
  {
4676
4764
  name: "Xiaomi",
4765
+ slug: "xiaomi",
4677
4766
  url: "https://platform.xiaomimimo.com/",
4678
4767
  keyName: "MIMO_API_KEY",
4679
4768
  authType: "APIKey",
@@ -4681,6 +4770,7 @@ var PROVIDERS = [
4681
4770
  },
4682
4771
  {
4683
4772
  name: "ZenMux",
4773
+ slug: "zenmux",
4684
4774
  url: "https://zenmux.ai/",
4685
4775
  keyName: "ZENMUX_API_KEY",
4686
4776
  authType: "APIKey",
@@ -4688,6 +4778,7 @@ var PROVIDERS = [
4688
4778
  },
4689
4779
  {
4690
4780
  name: "Zhipu AI",
4781
+ slug: "zhipu-ai",
4691
4782
  url: "https://open.bigmodel.cn/",
4692
4783
  keyName: "ZHIPU_API_KEY",
4693
4784
  authType: "APIKey",
@@ -4955,7 +5046,7 @@ class PiExecutorImpl {
4955
5046
  constructor(metadataManager) {
4956
5047
  this.metadataManager = metadataManager;
4957
5048
  }
4958
- async* execute(userPrompt, context) {
5049
+ async* execute(userPrompt, context, signal) {
4959
5050
  try {
4960
5051
  const providerName = context.provider;
4961
5052
  if (!providerName) {
@@ -5015,6 +5106,22 @@ class PiExecutorImpl {
5015
5106
  });
5016
5107
  const eventQueue = new EventQueue;
5017
5108
  let done = false;
5109
+ let aborted = false;
5110
+ const onAbort = () => {
5111
+ if (aborted)
5112
+ return;
5113
+ aborted = true;
5114
+ console.log("[ai] Client disconnected, aborting agent execution");
5115
+ done = true;
5116
+ eventQueue.notify();
5117
+ };
5118
+ if (signal) {
5119
+ if (signal.aborted) {
5120
+ console.log("[ai] Agent execution aborted before start");
5121
+ return;
5122
+ }
5123
+ signal.addEventListener("abort", onAbort, { once: true });
5124
+ }
5018
5125
  console.log(`[ai] Agent prompt request:`, {
5019
5126
  promptLength: userPrompt.length,
5020
5127
  promptPreview: userPrompt.substring(0, 200) + (userPrompt.length > 200 ? "..." : "")
@@ -5093,7 +5200,13 @@ class PiExecutorImpl {
5093
5200
  }
5094
5201
  }
5095
5202
  } finally {
5203
+ if (signal) {
5204
+ signal.removeEventListener("abort", onAbort);
5205
+ }
5096
5206
  unsubscribe();
5207
+ if (signal?.aborted) {
5208
+ console.warn("[ai] Agent aborted by user");
5209
+ }
5097
5210
  agent.abort();
5098
5211
  eventQueue.clearResolver();
5099
5212
  }
@@ -5226,6 +5339,12 @@ import { Hono } from "hono";
5226
5339
  import { stream } from "hono/streaming";
5227
5340
  function streamAsyncGenerator(c, generator, options) {
5228
5341
  return stream(c, async (writer) => {
5342
+ if (options?.abortController) {
5343
+ writer.onAbort(() => {
5344
+ console.log("[stream] Client disconnected, aborting stream");
5345
+ options.abortController?.abort();
5346
+ });
5347
+ }
5229
5348
  try {
5230
5349
  for await (const event of generator) {
5231
5350
  await writer.write(JSON.stringify(event) + `
@@ -6397,12 +6516,13 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
6397
6516
 
6398
6517
  Current message:
6399
6518
  User: ${content}` : content;
6519
+ const abortController = new AbortController;
6400
6520
  processingStateManager.setProcessing(threadId);
6401
6521
  async function* chatExecutionGenerator() {
6402
6522
  const capturedEvents = [];
6403
6523
  let fullContent = "";
6404
6524
  try {
6405
- for await (const event of agentExecutor.execute(promptWithContext, context)) {
6525
+ for await (const event of agentExecutor.execute(promptWithContext, context, abortController.signal)) {
6406
6526
  capturedEvents.push(event);
6407
6527
  if (event.type === "message" && event.content) {
6408
6528
  fullContent += event.content;
@@ -6448,7 +6568,7 @@ User: ${content}` : content;
6448
6568
  yield* chatExecutionGenerator();
6449
6569
  yield { type: "complete" };
6450
6570
  }
6451
- return streamAsyncGenerator(c, withCompleteSignal());
6571
+ return streamAsyncGenerator(c, withCompleteSignal(), { abortController });
6452
6572
  } catch (error) {
6453
6573
  return errorResponse(c, ErrorCodes.REQUEST_PARSE_ERROR, "Failed to parse request body", 400, error instanceof Error ? error.message : String(error));
6454
6574
  }
@@ -7009,7 +7129,8 @@ var PROVIDER_DATA = [
7009
7129
  id: "cerebras",
7010
7130
  models: [
7011
7131
  "gpt-oss-120b",
7012
- "zai-glm-4.7"
7132
+ "zai-glm-4.7",
7133
+ "qwen-3-235b-a22b-instruct-2507"
7013
7134
  ]
7014
7135
  },
7015
7136
  {
@@ -7417,6 +7538,7 @@ var GENERATED_DATA = [
7417
7538
  "cc-MiniMax-M2",
7418
7539
  "cc-deepseek-v3.1",
7419
7540
  "cc-ernie-4.5-300b-a47b",
7541
+ "cc-glm-5",
7420
7542
  "cc-kimi-k2-instruct",
7421
7543
  "cc-kimi-k2-instruct-0905",
7422
7544
  "cc-minimax-m2",
@@ -7447,9 +7569,13 @@ var GENERATED_DATA = [
7447
7569
  "coding-minimax-m2",
7448
7570
  "coding-minimax-m2-free",
7449
7571
  "coding-minimax-m2.1",
7572
+ "coding-minimax-m2.1-free",
7450
7573
  "coding-minimax-m2.5",
7574
+ "coding-minimax-m2.5-free",
7451
7575
  "coding-minimax-m2.5-highspeed",
7452
7576
  "deepseek-v3.2",
7577
+ "deepseek-v3.2-fast",
7578
+ "deepseek-v3.2-speciale",
7453
7579
  "deepseek-v3.2-think",
7454
7580
  "doubao-seed-1-8",
7455
7581
  "doubao-seed-2-0-code-preview",
@@ -7491,6 +7617,7 @@ var GENERATED_DATA = [
7491
7617
  "glm-4.6",
7492
7618
  "glm-4.7",
7493
7619
  "glm-4.7-flash-free",
7620
+ "glm-5",
7494
7621
  "gpt-4.1",
7495
7622
  "gpt-4.1-free",
7496
7623
  "gpt-4.1-mini",
@@ -7511,6 +7638,8 @@ var GENERATED_DATA = [
7511
7638
  "gpt-5.2-pro",
7512
7639
  "grok-4-1-fast-non-reasoning",
7513
7640
  "grok-4-1-fast-reasoning",
7641
+ "grok-4-fast-non-reasoning",
7642
+ "grok-4-fast-reasoning",
7514
7643
  "grok-code-fast-1",
7515
7644
  "inclusionAI/Ling-1T",
7516
7645
  "inclusionAI/Ling-flash-2.0",
@@ -7519,10 +7648,15 @@ var GENERATED_DATA = [
7519
7648
  "inclusionAI/Ring-flash-2.0",
7520
7649
  "kimi-for-coding-free",
7521
7650
  "kimi-k2-0711",
7651
+ "kimi-k2-instruct",
7522
7652
  "kimi-k2-thinking",
7523
7653
  "kimi-k2-turbo-preview",
7524
7654
  "llama-4-maverick",
7525
7655
  "llama-4-scout",
7656
+ "minimax-m2",
7657
+ "minimax-m2.1",
7658
+ "minimax-m2.5",
7659
+ "minimax-m2.5-highspeed",
7526
7660
  "o3",
7527
7661
  "o3-pro",
7528
7662
  "qwen3-235b-a22b",
@@ -7585,6 +7719,7 @@ var GENERATED_DATA = [
7585
7719
  "baidu/ernie-4.5-vl-28b-a3b",
7586
7720
  "bytedance-seed/seed-1.6",
7587
7721
  "bytedance-seed/seed-1.6-flash",
7722
+ "bytedance-seed/seed-2.0-mini",
7588
7723
  "cohere/command-r-08-2024",
7589
7724
  "cohere/command-r-plus-08-2024",
7590
7725
  "deepseek/deepseek-chat",
@@ -9769,6 +9904,32 @@ function createSlashCommandRoutes(router, threadManager) {
9769
9904
  router.delete("/api/slash-commands", (c) => deleteSlashCommand(c));
9770
9905
  }
9771
9906
 
9907
+ // src/features/ask-user/ask-user.routes.ts
9908
+ import { Hono as Hono11 } from "hono";
9909
+ function createAskUserRoutes() {
9910
+ const router = new Hono11;
9911
+ router.post("/respond", async (c) => {
9912
+ try {
9913
+ const body = await c.req.json();
9914
+ const { toolCallId, answer } = body;
9915
+ if (!toolCallId || typeof toolCallId !== "string") {
9916
+ return errorResponse(c, ErrorCodes.INVALID_REQUEST, "toolCallId is required and must be a string", 400);
9917
+ }
9918
+ if (answer === undefined || answer === null || typeof answer !== "string") {
9919
+ return errorResponse(c, ErrorCodes.INVALID_REQUEST, "answer is required and must be a string", 400);
9920
+ }
9921
+ const resolved = submitAnswer(toolCallId, answer);
9922
+ if (!resolved) {
9923
+ return errorResponse(c, ErrorCodes.INVALID_REQUEST, "No pending question found for this toolCallId", 404);
9924
+ }
9925
+ return c.json({ success: true });
9926
+ } catch (error) {
9927
+ return errorResponse(c, ErrorCodes.REQUEST_PARSE_ERROR, "Failed to parse request body", 400, error instanceof Error ? error.message : String(error));
9928
+ }
9929
+ });
9930
+ return router;
9931
+ }
9932
+
9772
9933
  // src/index.ts
9773
9934
  var __filename2 = fileURLToPath2(import.meta.url);
9774
9935
  var __dirname3 = path3.dirname(__filename2);
@@ -9779,7 +9940,7 @@ if (!isDebug) {
9779
9940
  console.log = () => {};
9780
9941
  }
9781
9942
  var port = isDebug ? 462 : process.env.PORT ? parseInt(process.env.PORT) : 641;
9782
- var app = new Hono11;
9943
+ var app = new Hono12;
9783
9944
  app.use("/*", cors());
9784
9945
  app.use("/*", async (c, next) => {
9785
9946
  if (c.req.path.startsWith("/api/")) {
@@ -9829,9 +9990,11 @@ app.route("/api/models", createModelRoutes(metadataManager));
9829
9990
  app.route("/api/git", createGitRoutes(metadataManager));
9830
9991
  app.route("/api/onboarding", createOnboardingRoutes(metadataManager));
9831
9992
  app.route("/api/scaffold", createScaffoldRoutes(projectManager));
9993
+ app.route("/api/ask-user", createAskUserRoutes());
9832
9994
  createSlashCommandRoutes(app, threadManager);
9833
- var isDev = true;
9834
- var publicDir = isDev ? path3.join(process.cwd(), "dist", "public") : path3.join(__dirname3, "public");
9995
+ var prodPublicDir = path3.join(__dirname3, "public");
9996
+ var devPublicDir = path3.join(process.cwd(), "dist", "public");
9997
+ var publicDir = fs2.existsSync(prodPublicDir) ? prodPublicDir : devPublicDir;
9835
9998
  var staticRoot = path3.relative(process.cwd(), publicDir);
9836
9999
  app.use("/*", async (c, next) => {
9837
10000
  if (c.req.path.startsWith("/api/")) {