chainlesschain 0.45.75 → 0.45.77

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 (76) hide show
  1. package/README.md +52 -15
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-sBrYoc3A.js → Analytics-Dd2DjBH5.js} +2 -2
  5. package/src/assets/web-panel/assets/AppLayout-CP9fATUN.js +1 -0
  6. package/src/assets/web-panel/assets/AppLayout-cxfKLu-m.css +1 -0
  7. package/src/assets/web-panel/assets/Backup-D6Tc7sf3.js +1 -0
  8. package/src/assets/web-panel/assets/Chat-DDUJZJ9I.js +1 -0
  9. package/src/assets/web-panel/assets/Chat-DfR76jyX.css +1 -0
  10. package/src/assets/web-panel/assets/Cowork-CPqYhoMI.css +1 -0
  11. package/src/assets/web-panel/assets/Cowork-XRFqGfqJ.js +48 -0
  12. package/src/assets/web-panel/assets/{Cron-CNs03iHJ.js → Cron-BnWzy_ZB.js} +2 -2
  13. package/src/assets/web-panel/assets/{Dashboard-DanoHPSI.js → Dashboard-D2vCkoGu.js} +1 -1
  14. package/src/assets/web-panel/assets/{Git-CCMVr3Y8.js → Git-DYlvK4sh.js} +2 -2
  15. package/src/assets/web-panel/assets/{Logs-BY6A0UNG.js → Logs-4VgUbfP0.js} +2 -2
  16. package/src/assets/web-panel/assets/{McpTools-CrBVYlg6.js → McpTools-ChaiHoWY.js} +2 -2
  17. package/src/assets/web-panel/assets/{Memory-CWx3SpUt.js → Memory-PFtpuOwf.js} +2 -2
  18. package/src/assets/web-panel/assets/{Notes-1LcGD49x.js → Notes-wc_n6Rh1.js} +2 -2
  19. package/src/assets/web-panel/assets/{Organization-Dx2DhbkM.js → Organization-D1qUa8NQ.js} +4 -4
  20. package/src/assets/web-panel/assets/{P2P-B16fjqfJ.js → P2P-DIG2gnR8.js} +2 -2
  21. package/src/assets/web-panel/assets/{Permissions-BQbC9FzG.js → Permissions-CpE-Ar1e.js} +3 -3
  22. package/src/assets/web-panel/assets/{Projects-CjhZbNYm.js → Projects-GjuS-C6U.js} +2 -2
  23. package/src/assets/web-panel/assets/{Providers-ivOAQtHM.js → Providers-CCfGeqh_.js} +2 -2
  24. package/src/assets/web-panel/assets/{RssFeed-BrsErdrU.js → RssFeed-5TkrXK7Z.js} +1 -1
  25. package/src/assets/web-panel/assets/{Security-DnEvJU5h.js → Security-CcfBWT1D.js} +3 -3
  26. package/src/assets/web-panel/assets/{Services-7jQywNbl.js → Services-Cnm5Zs5h.js} +1 -1
  27. package/src/assets/web-panel/assets/{Skills-CLlblJcG.js → Skills-BHapMb9h.js} +1 -1
  28. package/src/assets/web-panel/assets/{Tasks-CmJBC1cf.js → Tasks-DPb9OMck.js} +1 -1
  29. package/src/assets/web-panel/assets/Templates-Dij5t-rf.js +1 -0
  30. package/src/assets/web-panel/assets/{Wallet-3iYASEx_.js → Wallet-BJV5KmWA.js} +4 -4
  31. package/src/assets/web-panel/assets/{WebAuthn-s3Hzd9db.js → WebAuthn-DLkvYwSc.js} +5 -5
  32. package/src/assets/web-panel/assets/{antd-gZyc63Qr.js → antd-BQNxIyr-.js} +82 -82
  33. package/src/assets/web-panel/assets/github-dark-Dfs9RUU9.css +1 -0
  34. package/src/assets/web-panel/assets/index-CB5YlndO.js +2 -0
  35. package/src/assets/web-panel/assets/{markdown-Bv7nG63L.js → markdown-BeVIhIzs.js} +1 -1
  36. package/src/assets/web-panel/index.html +2 -2
  37. package/src/commands/learning.js +273 -0
  38. package/src/commands/lowcode.js +23 -8
  39. package/src/gateways/discord/discord-formatter.js +89 -0
  40. package/src/gateways/gateway-base.js +189 -0
  41. package/src/gateways/telegram/telegram-formatter.js +93 -0
  42. package/src/gateways/ws/action-protocol.js +54 -1
  43. package/src/gateways/ws/message-dispatcher.js +1 -0
  44. package/src/gateways/ws/ws-server.js +10 -1
  45. package/src/index.js +2 -0
  46. package/src/lib/app-builder.js +136 -8
  47. package/src/lib/autonomous-agent.js +8 -1
  48. package/src/lib/cli-context-engineering.js +15 -0
  49. package/src/lib/cowork-task-runner.js +101 -0
  50. package/src/lib/cowork-task-templates.js +493 -0
  51. package/src/lib/execution-backend.js +239 -0
  52. package/src/lib/hook-manager.js +2 -0
  53. package/src/lib/iteration-budget.js +175 -0
  54. package/src/lib/learning/learning-hooks.js +117 -0
  55. package/src/lib/learning/learning-tables.js +66 -0
  56. package/src/lib/learning/outcome-feedback.js +243 -0
  57. package/src/lib/learning/reflection-engine.js +323 -0
  58. package/src/lib/learning/skill-improver.js +536 -0
  59. package/src/lib/learning/skill-synthesizer.js +315 -0
  60. package/src/lib/learning/trajectory-store.js +409 -0
  61. package/src/lib/plugin-autodiscovery.js +224 -0
  62. package/src/lib/session-search.js +193 -0
  63. package/src/lib/sub-agent-context.js +7 -2
  64. package/src/lib/user-profile.js +172 -0
  65. package/src/lib/web-ui-server.js +1 -1
  66. package/src/repl/agent-repl.js +109 -0
  67. package/src/runtime/agent-core.js +75 -4
  68. package/src/runtime/coding-agent-contract-shared.cjs +35 -0
  69. package/src/runtime/coding-agent-policy.cjs +10 -0
  70. package/src/assets/web-panel/assets/AppLayout-2RCrdXxl.js +0 -1
  71. package/src/assets/web-panel/assets/AppLayout-D9pBLPC3.css +0 -1
  72. package/src/assets/web-panel/assets/Backup-D68fenbD.js +0 -1
  73. package/src/assets/web-panel/assets/Chat-B2nB8o_F.js +0 -1
  74. package/src/assets/web-panel/assets/Chat-DB46afPg.css +0 -1
  75. package/src/assets/web-panel/assets/Templates-RXT8-DNk.js +0 -1
  76. package/src/assets/web-panel/assets/index-CyGtHm63.js +0 -2
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Telegram Formatter — converts agent responses to Telegram-compatible markup.
3
+ *
4
+ * Telegram uses a subset of Markdown (MarkdownV2) with different escaping rules.
5
+ *
6
+ * @module telegram-formatter
7
+ */
8
+
9
+ // Characters that must be escaped in MarkdownV2
10
+ const ESCAPE_CHARS = /([_*[\]()~`>#+\-=|{}.!])/g;
11
+
12
+ /**
13
+ * Escape text for Telegram MarkdownV2.
14
+ * @param {string} text
15
+ * @returns {string}
16
+ */
17
+ export function escapeMarkdownV2(text) {
18
+ if (!text) return "";
19
+ return text.replace(ESCAPE_CHARS, "\\$1");
20
+ }
21
+
22
+ /**
23
+ * Convert standard Markdown to Telegram MarkdownV2.
24
+ * Handles: bold, italic, code blocks, inline code, links.
25
+ * @param {string} markdown
26
+ * @returns {string}
27
+ */
28
+ export function toTelegramMarkdown(markdown) {
29
+ if (!markdown) return "";
30
+
31
+ let result = markdown;
32
+
33
+ // Preserve code blocks (don't escape inside them)
34
+ const codeBlocks = [];
35
+ result = result.replace(/```[\s\S]*?```/g, (match) => {
36
+ codeBlocks.push(match);
37
+ return `__CODE_BLOCK_${codeBlocks.length - 1}__`;
38
+ });
39
+
40
+ // Preserve inline code
41
+ const inlineCode = [];
42
+ result = result.replace(/`[^`]+`/g, (match) => {
43
+ inlineCode.push(match);
44
+ return `__INLINE_CODE_${inlineCode.length - 1}__`;
45
+ });
46
+
47
+ // Escape special characters in text
48
+ result = escapeMarkdownV2(result);
49
+
50
+ // Restore code blocks and inline code (unescaped)
51
+ for (let i = codeBlocks.length - 1; i >= 0; i--) {
52
+ result = result.replace(
53
+ `__CODE_BLOCK_${i}__`.replace(ESCAPE_CHARS, "\\$1"),
54
+ codeBlocks[i],
55
+ );
56
+ }
57
+ for (let i = inlineCode.length - 1; i >= 0; i--) {
58
+ result = result.replace(
59
+ `__INLINE_CODE_${i}__`.replace(ESCAPE_CHARS, "\\$1"),
60
+ inlineCode[i],
61
+ );
62
+ }
63
+
64
+ return result;
65
+ }
66
+
67
+ /**
68
+ * Format an agent response for Telegram (plain text fallback).
69
+ * Strips complex markdown, keeps it readable.
70
+ * @param {string} response
71
+ * @param {object} [options]
72
+ * @param {number} [options.maxLength=4000]
73
+ * @returns {string}
74
+ */
75
+ export function formatForTelegram(response, options = {}) {
76
+ if (!response) return "";
77
+ const maxLength = options.maxLength || 4000;
78
+
79
+ let text = response;
80
+
81
+ // Convert headers to bold
82
+ text = text.replace(/^#{1,6}\s+(.+)$/gm, "*$1*");
83
+
84
+ // Keep code blocks as-is (Telegram supports ```)
85
+ // Keep bold (**text** → *text*)
86
+ text = text.replace(/\*\*(.+?)\*\*/g, "*$1*");
87
+
88
+ if (text.length > maxLength) {
89
+ text = text.substring(0, maxLength - 3) + "...";
90
+ }
91
+
92
+ return text;
93
+ }
@@ -1,3 +1,55 @@
1
+ export async function handleCoworkTask(server, id, ws, message) {
2
+ const { templateId = null, userMessage, files = [] } = message;
3
+
4
+ if (!userMessage || typeof userMessage !== "string") {
5
+ server._send(ws, {
6
+ id,
7
+ type: "error",
8
+ code: "INVALID_MESSAGE",
9
+ message: "userMessage field required",
10
+ });
11
+ return;
12
+ }
13
+
14
+ try {
15
+ const { runCoworkTask } = await import("../../lib/cowork-task-runner.js");
16
+
17
+ server._send(ws, {
18
+ id,
19
+ type: "cowork:started",
20
+ templateId,
21
+ });
22
+
23
+ const result = await runCoworkTask({
24
+ templateId,
25
+ userMessage,
26
+ files,
27
+ cwd: server.projectRoot || process.cwd(),
28
+ llmOptions: {},
29
+ });
30
+
31
+ server._send(ws, {
32
+ id,
33
+ type: "cowork:done",
34
+ taskId: result.taskId,
35
+ status: result.status,
36
+ templateId: result.templateId,
37
+ templateName: result.templateName,
38
+ summary: result.result?.summary || "",
39
+ artifacts: result.result?.artifacts || [],
40
+ toolsUsed: result.result?.toolsUsed || [],
41
+ iterationCount: result.result?.iterationCount || 0,
42
+ });
43
+ } catch (err) {
44
+ server._send(ws, {
45
+ id,
46
+ type: "error",
47
+ code: "COWORK_FAILED",
48
+ message: err.message,
49
+ });
50
+ }
51
+ }
52
+
1
53
  export function handleSlashCommand(server, id, ws, message) {
2
54
  const { sessionId, command } = message;
3
55
  const handler = server.sessionHandlers.get(sessionId);
@@ -36,7 +88,8 @@ export async function handleOrchestrate(server, id, ws, message) {
36
88
  }
37
89
 
38
90
  try {
39
- const { Orchestrator, TASK_SOURCE } = await import("../../lib/orchestrator.js");
91
+ const { Orchestrator, TASK_SOURCE } =
92
+ await import("../../lib/orchestrator.js");
40
93
 
41
94
  const orch = new Orchestrator({
42
95
  cwd: cwd || server.projectRoot || process.cwd(),
@@ -43,6 +43,7 @@ export function createWsMessageDispatcher(server) {
43
43
  "session-answer": () => server._handleSessionAnswer(id, ws, message),
44
44
  "host-tool-result": () => server._handleHostToolResult(id, ws, message),
45
45
  orchestrate: () => server._handleOrchestrate(id, ws, message),
46
+ "cowork-task": () => server._handleCoworkTask(id, ws, message),
46
47
  "tasks-list": () => server._handleTasksList(id, ws),
47
48
  "tasks-stop": () => server._handleTasksStop(id, ws, message),
48
49
  "tasks-detail": () => server._handleTaskDetail(id, ws, message),
@@ -48,7 +48,11 @@ import {
48
48
  handleTaskGraphAdvance,
49
49
  handleTaskGraphState,
50
50
  } from "./session-protocol.js";
51
- import { handleSlashCommand, handleOrchestrate } from "./action-protocol.js";
51
+ import {
52
+ handleSlashCommand,
53
+ handleOrchestrate,
54
+ handleCoworkTask,
55
+ } from "./action-protocol.js";
52
56
  import {
53
57
  handleWorktreeDiff,
54
58
  handleWorktreeMerge,
@@ -294,6 +298,11 @@ export class ChainlessChainWSServer extends EventEmitter {
294
298
  return handleOrchestrate(this, id, ws, message);
295
299
  }
296
300
 
301
+ /** @private — run a cowork daily task via SubAgentContext */
302
+ async _handleCoworkTask(id, ws, message) {
303
+ return handleCoworkTask(this, id, ws, message);
304
+ }
305
+
297
306
  /** @private – list background tasks */
298
307
  async _handleTasksList(id, ws) {
299
308
  try {
package/src/index.js CHANGED
@@ -47,6 +47,7 @@ import { registerA2aCommand } from "./commands/a2a.js";
47
47
  // Phase 7: Security & Evolution
48
48
  import { registerSandboxCommand } from "./commands/sandbox.js";
49
49
  import { registerEvolutionCommand } from "./commands/evolution.js";
50
+ import { registerLearningCommand } from "./commands/learning.js";
50
51
 
51
52
  // Phase 7: EvoMap Federation + DAO Governance
52
53
  import { registerDaoCommand } from "./commands/dao.js";
@@ -168,6 +169,7 @@ export function createProgram() {
168
169
  // Phase 7: Security & Evolution
169
170
  registerSandboxCommand(program);
170
171
  registerEvolutionCommand(program);
172
+ registerLearningCommand(program);
171
173
 
172
174
  // Phase 7: EvoMap Federation + DAO Governance
173
175
  registerDaoCommand(program);
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  import crypto from "crypto";
7
+ import fs from "fs";
8
+ import path from "path";
7
9
 
8
10
  /** @type {Map<string, object>} In-memory app cache */
9
11
  const _apps = new Map();
@@ -220,14 +222,12 @@ export function saveDesign(db, appId, design) {
220
222
  ).run(versionId, appId, newVersion, JSON.stringify(design));
221
223
 
222
224
  if (!_versions.has(appId)) _versions.set(appId, []);
223
- _versions
224
- .get(appId)
225
- .push({
226
- id: versionId,
227
- app_id: appId,
228
- version: newVersion,
229
- snapshot: design,
230
- });
225
+ _versions.get(appId).push({
226
+ id: versionId,
227
+ app_id: appId,
228
+ version: newVersion,
229
+ snapshot: design,
230
+ });
231
231
 
232
232
  if (_apps.has(appId)) {
233
233
  _apps.get(appId).design = design;
@@ -375,3 +375,131 @@ export function listApps(db) {
375
375
  updated_at: r.updated_at,
376
376
  }));
377
377
  }
378
+
379
+ /**
380
+ * Get a single application by ID from the database.
381
+ *
382
+ * @param {object} db
383
+ * @param {string} appId
384
+ * @returns {object|null}
385
+ */
386
+ export function getApp(db, appId) {
387
+ const row = db.prepare(`SELECT * FROM lowcode_apps WHERE id = ?`).get(appId);
388
+ if (!row) return null;
389
+ return {
390
+ id: row.id,
391
+ name: row.name,
392
+ description: row.description,
393
+ design: row.design
394
+ ? JSON.parse(row.design)
395
+ : { components: [], layout: {} },
396
+ status: row.status,
397
+ version: row.version,
398
+ platform: row.platform,
399
+ };
400
+ }
401
+
402
+ /**
403
+ * Generate a static HTML bundle string for an app's design.
404
+ *
405
+ * @param {object} app - App object with design
406
+ * @returns {{ "index.html": string, "app.js": string, "style.css": string }}
407
+ */
408
+ function generateStaticBundle(app) {
409
+ const design = app.design || { components: [], layout: {} };
410
+ const components = design.components || [];
411
+
412
+ const componentHtml = components
413
+ .map(
414
+ (c, i) =>
415
+ ` <div class="lc-component lc-${(c.type || c.name || "widget").toLowerCase()}" id="comp-${i}">${c.label || c.type || c.name || "Component"}</div>`,
416
+ )
417
+ .join("\n");
418
+
419
+ const html = `<!DOCTYPE html>
420
+ <html lang="en">
421
+ <head>
422
+ <meta charset="UTF-8">
423
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
424
+ <title>${app.name || "Low-Code App"}</title>
425
+ <link rel="stylesheet" href="style.css">
426
+ </head>
427
+ <body>
428
+ <div id="app">
429
+ <h1>${app.name || "Low-Code App"}</h1>
430
+ <p>${app.description || ""}</p>
431
+ <div class="lc-container">
432
+ ${componentHtml || " <p>No components defined</p>"}
433
+ </div>
434
+ </div>
435
+ <script src="app.js"><\/script>
436
+ </body>
437
+ </html>`;
438
+
439
+ const css = `/* Generated by ChainlessChain Low-Code Platform */
440
+ * { margin: 0; padding: 0; box-sizing: border-box; }
441
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 24px; background: #f5f5f5; }
442
+ #app { max-width: 1200px; margin: 0 auto; }
443
+ h1 { margin-bottom: 8px; color: #1a1a1a; }
444
+ p { margin-bottom: 16px; color: #666; }
445
+ .lc-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
446
+ .lc-component { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; }
447
+ `;
448
+
449
+ const js = `// Generated by ChainlessChain Low-Code Platform
450
+ // App: ${app.name || "Untitled"} v${app.version || 1}
451
+ (function() {
452
+ console.log("Low-Code App initialized: ${app.name || "Untitled"}");
453
+ var config = ${JSON.stringify({ id: app.id, name: app.name, version: app.version, platform: app.platform })};
454
+ document.querySelectorAll(".lc-component").forEach(function(el) {
455
+ el.addEventListener("click", function() { el.classList.toggle("active"); });
456
+ });
457
+ })();
458
+ `;
459
+
460
+ return { "index.html": html, "app.js": js, "style.css": css };
461
+ }
462
+
463
+ /**
464
+ * Deploy an application by generating a static bundle and writing it to disk.
465
+ *
466
+ * @param {object} db
467
+ * @param {string} appId
468
+ * @param {{ outputDir?: string }} options
469
+ * @returns {{ appId: string, outputDir: string, files: string[], deployedAt: string }}
470
+ */
471
+ export function deployApp(db, appId, options = {}) {
472
+ const app = getApp(db, appId);
473
+ if (!app) {
474
+ throw new Error(`App '${appId}' not found`);
475
+ }
476
+ if (app.status !== "published") {
477
+ throw new Error(
478
+ `App '${appId}' must be published before deploying (current status: ${app.status})`,
479
+ );
480
+ }
481
+
482
+ const outputDir =
483
+ options.outputDir ||
484
+ path.join(process.cwd(), ".chainlesschain", "deploys", appId);
485
+ fs.mkdirSync(outputDir, { recursive: true });
486
+
487
+ const bundle = generateStaticBundle(app);
488
+ const fileNames = [];
489
+ for (const [fileName, content] of Object.entries(bundle)) {
490
+ fs.writeFileSync(path.join(outputDir, fileName), content, "utf-8");
491
+ fileNames.push(fileName);
492
+ }
493
+
494
+ // Update app status to deployed
495
+ db.prepare(
496
+ `UPDATE lowcode_apps SET status = ?, updated_at = datetime('now') WHERE id = ?`,
497
+ ).run("deployed", appId);
498
+
499
+ if (_apps.has(appId)) {
500
+ _apps.get(appId).status = "deployed";
501
+ }
502
+
503
+ const deployedAt = new Date().toISOString();
504
+ return { appId, outputDir, files: fileNames, deployedAt };
505
+ }
@@ -52,11 +52,18 @@ export class CLIAutonomousAgent extends EventEmitter {
52
52
  /**
53
53
  * Initialize with required dependencies.
54
54
  */
55
- initialize({ llmChat, toolExecutor, hookManager, maxIterations } = {}) {
55
+ initialize({
56
+ llmChat,
57
+ toolExecutor,
58
+ hookManager,
59
+ maxIterations,
60
+ iterationBudget,
61
+ } = {}) {
56
62
  this._llmChat = llmChat || null;
57
63
  this._toolExecutor = toolExecutor || null;
58
64
  this._hookManager = hookManager || null;
59
65
  if (maxIterations) this._maxIterations = maxIterations;
66
+ this._iterationBudget = iterationBudget || null; // shared budget from caller
60
67
  this._initialized = true;
61
68
  }
62
69
 
@@ -12,6 +12,7 @@ import { generateInstinctPrompt } from "./instinct-manager.js";
12
12
  import { recallMemory } from "./hierarchical-memory.js";
13
13
  import { BM25Search } from "./bm25-search.js";
14
14
  import { createHash } from "crypto";
15
+ import { readUserProfile } from "./user-profile.js";
15
16
 
16
17
  // Exported for test injection
17
18
  export const _deps = {
@@ -19,6 +20,7 @@ export const _deps = {
19
20
  recallMemory,
20
21
  BM25Search,
21
22
  createHash,
23
+ readUserProfile,
22
24
  };
23
25
 
24
26
  // ─── System prompt cleaning regexes (match desktop KV-Cache optimization) ───
@@ -105,6 +107,19 @@ export class CLIContextEngineering {
105
107
  }
106
108
  }
107
109
 
110
+ // 2b. User profile injection (USER.md)
111
+ try {
112
+ const profile = _deps.readUserProfile();
113
+ if (profile) {
114
+ result.push({
115
+ role: "system",
116
+ content: `## User Profile\n${profile}`,
117
+ });
118
+ }
119
+ } catch (_err) {
120
+ // User profile injection failed — skip silently
121
+ }
122
+
108
123
  // 3. Memory injection (scoped: higher threshold, namespace-aware)
109
124
  if (this.db && userQuery) {
110
125
  try {
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Cowork Task Runner — executes daily tasks using SubAgentContext.
3
+ *
4
+ * Creates an isolated sub-agent with a template-specific system prompt,
5
+ * runs the agent loop, and yields progress events for WS consumers.
6
+ *
7
+ * @module cowork-task-runner
8
+ */
9
+
10
+ import { SubAgentContext } from "./sub-agent-context.js";
11
+ import { getTemplate } from "./cowork-task-templates.js";
12
+
13
+ // ─── Constants ────────────────────────────────────────────────────────────────
14
+
15
+ const DEFAULT_MAX_ITERATIONS = 50;
16
+ const DEFAULT_TOKEN_BUDGET = 100_000;
17
+
18
+ // ─── Runner ───────────────────────────────────────────────────────────────────
19
+
20
+ /**
21
+ * Run a cowork task using SubAgentContext.
22
+ *
23
+ * @param {object} options
24
+ * @param {string|null} options.templateId - Template ID (null = free mode)
25
+ * @param {string} options.userMessage - User's task description
26
+ * @param {string[]} [options.files] - File paths provided by user
27
+ * @param {string} [options.cwd] - Working directory
28
+ * @param {object} [options.db] - Database instance
29
+ * @param {object} [options.llmOptions] - LLM provider/model/key
30
+ * @param {number} [options.maxIterations] - Override iteration limit
31
+ * @param {number} [options.tokenBudget] - Override token budget
32
+ * @returns {Promise<{ taskId: string, status: string, result: object }>}
33
+ */
34
+ export async function runCoworkTask(options = {}) {
35
+ const {
36
+ templateId = null,
37
+ userMessage,
38
+ files = [],
39
+ cwd = process.cwd(),
40
+ db = null,
41
+ llmOptions = {},
42
+ maxIterations = DEFAULT_MAX_ITERATIONS,
43
+ tokenBudget = DEFAULT_TOKEN_BUDGET,
44
+ } = options;
45
+
46
+ if (!userMessage || typeof userMessage !== "string") {
47
+ throw new Error("userMessage is required");
48
+ }
49
+
50
+ // Resolve template
51
+ const template = getTemplate(templateId);
52
+
53
+ // Build the task prompt with template context + files
54
+ const taskParts = [template.systemPromptExtension];
55
+
56
+ if (files.length > 0) {
57
+ taskParts.push(`\n## 用户提供的文件\n${files.join("\n")}`);
58
+ }
59
+
60
+ const task = taskParts.join("\n");
61
+
62
+ // Create isolated sub-agent context
63
+ const subAgent = SubAgentContext.create({
64
+ role: `cowork-${template.id}`,
65
+ task,
66
+ inheritedContext: null,
67
+ maxIterations,
68
+ tokenBudget,
69
+ db,
70
+ llmOptions,
71
+ cwd,
72
+ });
73
+
74
+ const taskId = subAgent.id;
75
+
76
+ // Run the agent with the user's message
77
+ try {
78
+ const result = await subAgent.run(userMessage);
79
+ return {
80
+ taskId,
81
+ status: subAgent.status,
82
+ templateId: template.id,
83
+ templateName: template.name,
84
+ result,
85
+ };
86
+ } catch (err) {
87
+ return {
88
+ taskId,
89
+ status: "failed",
90
+ templateId: template.id,
91
+ templateName: template.name,
92
+ result: {
93
+ summary: `Task failed: ${err.message}`,
94
+ artifacts: [],
95
+ tokenCount: 0,
96
+ toolsUsed: [],
97
+ iterationCount: 0,
98
+ },
99
+ };
100
+ }
101
+ }