@lovelybunch/api 1.0.76-alpha.9 → 1.0.77-alpha.0

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 (173) hide show
  1. package/dist/lib/git.d.ts +3 -0
  2. package/dist/lib/git.js +35 -0
  3. package/dist/lib/terminal/terminal-manager.js +2 -1
  4. package/dist/routes/api/v1/ai/route.js +214 -152
  5. package/dist/routes/api/v1/config/route.js +13 -9
  6. package/dist/routes/api/v1/context/agents/route.d.ts +3 -0
  7. package/dist/routes/api/v1/context/agents/route.js +159 -0
  8. package/dist/routes/api/v1/context/index.js +6 -4
  9. package/dist/routes/api/v1/context/memory/route.d.ts +3 -0
  10. package/dist/routes/api/v1/context/memory/route.js +163 -0
  11. package/dist/routes/api/v1/context/team/route.d.ts +3 -0
  12. package/dist/routes/api/v1/context/team/route.js +159 -0
  13. package/dist/routes/api/v1/events/status/route.d.ts +1 -1
  14. package/dist/routes/api/v1/git/index.js +27 -1
  15. package/dist/routes/api/v1/jobs/[id]/route.d.ts +26 -32
  16. package/dist/routes/api/v1/jobs/[id]/route.js +3 -3
  17. package/dist/routes/api/v1/jobs/[id]/run/route.d.ts +2 -2
  18. package/dist/routes/api/v1/jobs/[id]/runs/[runId]/route.d.ts +2 -2
  19. package/dist/routes/api/v1/jobs/route.d.ts +33 -33
  20. package/dist/routes/api/v1/jobs/route.js +17 -2
  21. package/dist/routes/api/v1/jobs/status/route.d.ts +1 -1
  22. package/dist/routes/api/v1/mail/route.d.ts +3 -3
  23. package/dist/routes/api/v1/mcp/index.js +194 -21
  24. package/dist/routes/api/v1/resources/generate/route.js +10 -3
  25. package/dist/routes/api/v1/slack/route.d.ts +6 -6
  26. package/dist/routes/api/v1/tasks/[id]/route.d.ts +4 -4
  27. package/dist/routes/api/v1/tasks/[id]/steps/[stepId]/route.d.ts +2 -2
  28. package/dist/routes/api/v1/tasks/route.d.ts +1 -1
  29. package/dist/server-with-static.js +23 -17
  30. package/dist/server.js +1 -0
  31. package/package.json +24 -23
  32. package/static/assets/ActivityPage-rASRHKYj.js +1 -0
  33. package/static/assets/AgentsContextEditPage-CMWD-7mS.js +9 -0
  34. package/static/assets/AgentsContextPage-VfMmxxr6.js +1 -0
  35. package/static/assets/{ApiKeysSettingsPage-CjrCuHHE.js → ApiKeysSettingsPage-DxtuyXwn.js} +2 -2
  36. package/static/assets/{AuthSettingsPage-elJX4TCV.js → AuthSettingsPage-CWTpS6BA.js} +2 -2
  37. package/static/assets/{CallbackPage-CUuZYgBL.js → CallbackPage-CjlTVuif.js} +1 -1
  38. package/static/assets/CodePage-DwjtGxQi.js +2 -0
  39. package/static/assets/{CollapsibleSection-DfTxWX0X.js → CollapsibleSection-LrQJlSxD.js} +1 -1
  40. package/static/assets/{DashboardPage-CmS8UPSm.js → DashboardPage-DnyFyDO0.js} +9 -19
  41. package/static/assets/{GitPage-DSfn76oO.js → GitPage-o37iQMYQ.js} +3 -3
  42. package/static/assets/{GitSettingsPage-Bu8h7hcr.js → GitSettingsPage-O0fe5kxm.js} +2 -2
  43. package/static/assets/{IdentityPage-NFKq2FHi.js → IdentityPage-KWjruliO.js} +2 -2
  44. package/static/assets/{ImplementationStepsEditor-Cn-MshnR.js → ImplementationStepsEditor-h_Mxu1tF.js} +2 -2
  45. package/static/assets/IntegrationsSettingsPage-DeFQd6af.js +1 -0
  46. package/static/assets/JobDetailPage-HOLpF7Sx.js +1 -0
  47. package/static/assets/KnowledgeDetailPage-C5egPQ54.js +1 -0
  48. package/static/assets/KnowledgeEditPage-DrdvEGuR.js +1 -0
  49. package/static/assets/{KnowledgePage-C3zoPEcH.js → KnowledgePage-CK5rtZcv.js} +2 -2
  50. package/static/assets/{LoginPage-BYNLGEnz.js → LoginPage-DajEouCN.js} +1 -1
  51. package/static/assets/MailInboxPage-CewILL7o.js +1 -0
  52. package/static/assets/MailProcessingModal-B1ZLXyYl.js +1 -0
  53. package/static/assets/MailReadPage--LnQ743i.js +1 -0
  54. package/static/assets/MailSentPage-BqO4iBq6.js +1 -0
  55. package/static/assets/{McpSettingsPage-CfhBioEH.js → McpSettingsPage-BZ6GJOtk.js} +1 -1
  56. package/static/assets/MemoryEditPage-BLfdKEDu.js +13 -0
  57. package/static/assets/MemoryPage-BV0RlvUS.js +1 -0
  58. package/static/assets/{NewKnowledgePage-BUwh_DFd.js → NewKnowledgePage-SrBhXpH6.js} +1 -1
  59. package/static/assets/{NewSkillPage-B2rztwzD.js → NewSkillPage-vt-0rIkv.js} +1 -1
  60. package/static/assets/{NewTaskPage-Cvw20nug.js → NewTaskPage-CBUpwIFP.js} +2 -2
  61. package/static/assets/{NotFoundPage-CXvLVHsT.js → NotFoundPage-CWF5qOC0.js} +1 -1
  62. package/static/assets/{NotificationsSettingsPage-Df_GZ7_p.js → NotificationsSettingsPage-3ZvMrqiq.js} +1 -1
  63. package/static/assets/{PromptsSettingsPage-QTxJgO5-.js → PromptsSettingsPage-pHKoiUPI.js} +1 -1
  64. package/static/assets/{ResourceDetailPage-C9RHHVKu.js → ResourceDetailPage-CE4iDbv7.js} +1 -1
  65. package/static/assets/ResourcesPage-4_5NA4RF.js +41 -0
  66. package/static/assets/{RoleEditPage-N2-v2MXN.js → RoleEditPage-4sSELIRX.js} +1 -1
  67. package/static/assets/{RolePage-Da0kZMUH.js → RolePage-Ds17xn4X.js} +1 -1
  68. package/static/assets/{RulesSettingsPage-CSd5iz22.js → RulesSettingsPage-DnyGaden.js} +1 -1
  69. package/static/assets/RunDetailPage-D2ajvKfh.js +1 -0
  70. package/static/assets/SchedulePage-oEGct1B1.js +4 -0
  71. package/static/assets/{SkillDetailPage-QjqBAuFU.js → SkillDetailPage-mSypV_JK.js} +1 -1
  72. package/static/assets/{SkillEditPage-BB0x4VVH.js → SkillEditPage-DeI8uu3S.js} +1 -1
  73. package/static/assets/{SkillsPage-BMDNxMmZ.js → SkillsPage-Bb_dEdun.js} +2 -2
  74. package/static/assets/{SkillsSettingsPage-DcdeOlAl.js → SkillsSettingsPage-CMWf2O9y.js} +1 -1
  75. package/static/assets/SourceInput-CtyUOXOG.js +1 -0
  76. package/static/assets/{TagInput-bxFcAknC.js → TagInput-C0NyMxlY.js} +1 -1
  77. package/static/assets/{TaskDetailPage-CAmREdfh.js → TaskDetailPage-6DDI0juh.js} +1 -1
  78. package/static/assets/{TaskEditPage-BDgZtG36.js → TaskEditPage-Btc1NXtR.js} +1 -1
  79. package/static/assets/{TasksPage-BQ6eLw0T.js → TasksPage-C9TYvqRz.js} +3 -3
  80. package/static/assets/TeamEditPage-CLvNy6Ss.js +9 -0
  81. package/static/assets/TeamPage-DSWax4fa.js +1 -0
  82. package/static/assets/{TerminalPage-BhWGRGf1.js → TerminalPage-DNDHEYJZ.js} +1 -1
  83. package/static/assets/{TerminalSessionPage-Bu-WiE0d.js → TerminalSessionPage-nNCw_oDE.js} +2 -2
  84. package/static/assets/{UserPreferencesPage-B_Tu1a8h.js → UserPreferencesPage-jc1SA79x.js} +1 -1
  85. package/static/assets/UserSettingsPage-pr6n15Pz.js +1 -0
  86. package/static/assets/UtilitiesPage-CjLb989o.js +1 -0
  87. package/static/assets/{alert-C42EBWiI.js → alert-BtkLXQ3p.js} +1 -1
  88. package/static/assets/{arrow-down-CuFFg288.js → arrow-down-DnNLmXEs.js} +1 -1
  89. package/static/assets/{arrow-left-C8VbyW3d.js → arrow-left-B9NBHEkS.js} +1 -1
  90. package/static/assets/{arrow-up-down-GN3qK2mU.js → arrow-up-down-DIuMvAne.js} +1 -1
  91. package/static/assets/{arrow-up-vC1c-5gW.js → arrow-up-eMUJY7J9.js} +1 -1
  92. package/static/assets/{badge-CKwUSKzT.js → badge-BbfU_aPt.js} +1 -1
  93. package/static/assets/{browser-modal-C9VBJ_El.js → browser-modal-J9l3o5os.js} +2 -2
  94. package/static/assets/{card-DRBgMGuU.js → card-CL5bB4cs.js} +1 -1
  95. package/static/assets/{chevron-left-2sP4ienq.js → chevron-left-B-7K6MNx.js} +1 -1
  96. package/static/assets/{chevron-up-DJfyvHso.js → chevron-up-BwTmMEW2.js} +1 -1
  97. package/static/assets/{chevrons-up-DfFpzHXf.js → chevrons-up-jI6nxhrz.js} +1 -1
  98. package/static/assets/{circle-alert-DP4rtmEK.js → circle-alert-vJFSz39V.js} +1 -1
  99. package/static/assets/{circle-check-Cnp-EoKX.js → circle-check-D2LVHMl-.js} +1 -1
  100. package/static/assets/{circle-check-big-3mrGMFVn.js → circle-check-big-BwQ_q1N7.js} +1 -1
  101. package/static/assets/{circle-play-7F5AVehH.js → circle-play-CLKDBkrK.js} +1 -1
  102. package/static/assets/{circle-x-DxbiyJ6w.js → circle-x-CuUVLiI-.js} +1 -1
  103. package/static/assets/{clipboard-CQNR4_yT.js → clipboard-BHOFelnW.js} +1 -1
  104. package/static/assets/{clock-DGw3R_UL.js → clock-D-X3KCw6.js} +1 -1
  105. package/static/assets/{code-DSNMK8tD.js → code-DqYaanki.js} +1 -1
  106. package/static/assets/{download-BWL6PFiw.js → download-DeEju9jg.js} +1 -1
  107. package/static/assets/{external-link-BqUE-DMN.js → external-link-CrRz0sU-.js} +1 -1
  108. package/static/assets/{eye-BD57N1qQ.js → eye-Cx9ZGkg5.js} +1 -1
  109. package/static/assets/{folder-git-2-CW5Zi8Bm.js → folder-git-2-Cizv1NA6.js} +1 -1
  110. package/static/assets/globe-lpD9Jv31.js +6 -0
  111. package/static/assets/{index-BWktaW8U.js → index-BCYTbJRb.js} +1 -1
  112. package/static/assets/{index-BArpuuuE.js → index-BL-5Bhtg.js} +1 -1
  113. package/static/assets/{index-EpelXypg.js → index-BPdWQ0rI.js} +1 -1
  114. package/static/assets/{index-CheR43vH.js → index-BVrOTqTm.js} +1 -1
  115. package/static/assets/{index-uxv5vQZF.js → index-BknCCMZK.js} +1 -1
  116. package/static/assets/{index-CVVL2h9f.js → index-BvZJRqTz.js} +1 -1
  117. package/static/assets/index-ByTA2ZiD.js +497 -0
  118. package/static/assets/{index-CMt1ExW-.js → index-CMaK0hpv.js} +1 -1
  119. package/static/assets/{index-CMQcknsg.js → index-CkT4WgqR.js} +1 -1
  120. package/static/assets/{index-Bedu89qc.js → index-CuLP7P_G.js} +1 -1
  121. package/static/assets/{index-B9OOTqx4.js → index-D12O6wM3.js} +1 -1
  122. package/static/assets/{index-stPObw-o.js → index-DIt703WU.js} +1 -1
  123. package/static/assets/{index-CXWGFCS-.js → index-DK1gGyTZ.js} +1 -1
  124. package/static/assets/index-DVMcu9sQ.css +1 -0
  125. package/static/assets/{index-BG5vEV31.js → index-Dc9Njo8L.js} +1 -1
  126. package/static/assets/{index-B0fLM-4F.js → index-DgAcL75U.js} +1 -1
  127. package/static/assets/{index-BmTTXfmk.js → index-Djzp98Vj.js} +1 -1
  128. package/static/assets/{index-DO47L-WG.js → index-Dnj5cWsp.js} +1 -1
  129. package/static/assets/{index-X4fPPLHI.js → index-Hy3cH93B.js} +1 -1
  130. package/static/assets/{index-D4awulBm.js → index-ihWq-CVE.js} +1 -1
  131. package/static/assets/{info-DMJxIp7i.js → info-DxhTCbw1.js} +1 -1
  132. package/static/assets/{label-DxDedS8x.js → label-Dp0-28_O.js} +1 -1
  133. package/static/assets/{markdown-editor-DJ2CscSp.js → markdown-editor-DUmrf1eN.js} +3 -3
  134. package/static/assets/{message-square-DF58cqKJ.js → message-square-BPOApeM3.js} +1 -1
  135. package/static/assets/{paperclip-CHyfSVll.js → paperclip-Bfjc1WLZ.js} +1 -1
  136. package/static/assets/{pause-B-ouFhaQ.js → pause-Bfz-QQZp.js} +1 -1
  137. package/static/assets/{pipeline-builders-Bkf0wt_O.js → pipeline-builders-DrEjlsbH.js} +1 -1
  138. package/static/assets/{play-B0y1weCV.js → play-fOwEoIcu.js} +1 -1
  139. package/static/assets/{radio-group-DFNQiPDU.js → radio-group-B8RB7N01.js} +1 -1
  140. package/static/assets/{refresh-cw-5y5jTc6x.js → refresh-cw-Cj_5MZiJ.js} +1 -1
  141. package/static/assets/{search-B5deRThk.js → search-BgbqRUnf.js} +1 -1
  142. package/static/assets/{select-BzOa_wZx.js → select-CfwLZl55.js} +1 -1
  143. package/static/assets/server-u9FLHclt.js +6 -0
  144. package/static/assets/{switch-Bf2z8aaj.js → switch-CX_Inx_p.js} +1 -1
  145. package/static/assets/{tabs-DkdBSep3.js → tabs-Bj0YyeRI.js} +1 -1
  146. package/static/assets/{tag-gRsTk0jY.js → tag-rYG4CdRx.js} +1 -1
  147. package/static/assets/terminal-preview-BNm5-Umi.js +1 -0
  148. package/static/assets/triangle-alert-C0ovMJwZ.js +6 -0
  149. package/static/assets/{use-terminal-D6Qe1nwM.js → use-terminal-D1UnvAVs.js} +1 -1
  150. package/static/assets/{video-DXo9sCfT.js → video-CuyRES-H.js} +1 -1
  151. package/static/index.html +2 -2
  152. package/static/assets/ActivityPage-BaSoKgwZ.js +0 -1
  153. package/static/assets/ArchitectureEditPage-MYU7btLs.js +0 -21
  154. package/static/assets/ArchitecturePage-CGqWy8b1.js +0 -1
  155. package/static/assets/CodePage-C_bzfOnI.js +0 -2
  156. package/static/assets/IntegrationsSettingsPage-Cw6ItrNf.js +0 -1
  157. package/static/assets/JobDetailPage-DLSD4HiU.js +0 -1
  158. package/static/assets/KnowledgeDetailPage-DY8PpD9X.js +0 -1
  159. package/static/assets/KnowledgeEditPage-B28N-7vM.js +0 -1
  160. package/static/assets/MailInboxPage-DDYg0W70.js +0 -1
  161. package/static/assets/MailProcessingModal-CXIrtSz0.js +0 -6
  162. package/static/assets/MailReadPage-CVz6oD-y.js +0 -1
  163. package/static/assets/MailSentPage-ClaVWZCR.js +0 -1
  164. package/static/assets/ProjectEditPage-DKf6ZoiG.js +0 -11
  165. package/static/assets/ProjectPage-CHhx9s1k.js +0 -1
  166. package/static/assets/ResourcesPage-BI8tfYZ2.js +0 -41
  167. package/static/assets/SchedulePage-CS8g2Db-.js +0 -4
  168. package/static/assets/SourceInput-rc4KSqcv.js +0 -1
  169. package/static/assets/UserSettingsPage-BnEGtrGo.js +0 -1
  170. package/static/assets/UtilitiesPage-k7ABCAig.js +0 -1
  171. package/static/assets/index-Cht-fo9a.css +0 -1
  172. package/static/assets/index-DB2Tq9wz.js +0 -487
  173. package/static/assets/terminal-preview-BfrUN2ok.js +0 -1
package/dist/lib/git.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { InstructionFileKind, InstructionFiles } from '@lovelybunch/types';
1
2
  export declare function getRepoRoot(): Promise<string>;
2
3
  export declare function getWorktreesBase(): Promise<string>;
3
4
  export declare function sanitizeBranchName(name: string): string;
@@ -9,6 +10,8 @@ export declare function runGit(args: string[], opts?: {
9
10
  stdout: string;
10
11
  stderr: string;
11
12
  }>;
13
+ export declare function lsTree(ref?: string): Promise<string[]>;
14
+ export declare function getInstructionFiles(filter?: InstructionFileKind | 'all'): Promise<InstructionFiles>;
12
15
  export declare function getRepoStatus(): Promise<{
13
16
  branch: string;
14
17
  ahead: number;
package/dist/lib/git.js CHANGED
@@ -5,6 +5,7 @@ import { promises as fs } from 'fs';
5
5
  import { readGithubToken, isGithubTokenValid } from './github-token.js';
6
6
  import { findGaitDirectory } from './gait-path.js';
7
7
  import { loadGitSettings } from './git-settings.js';
8
+ import { INSTRUCTION_FILE_KINDS } from '@lovelybunch/types';
8
9
  const execFile = promisify(_execFile);
9
10
  // Base directory for worktrees under the repository root
10
11
  export async function getRepoRoot() {
@@ -52,6 +53,40 @@ export async function runGit(args, opts) {
52
53
  };
53
54
  return execFile('git', args, { cwd, maxBuffer: 10 * 1024 * 1024, timeout, env });
54
55
  }
56
+ // --- ls-tree ---
57
+ export async function lsTree(ref = 'HEAD') {
58
+ const { stdout } = await runGit(['ls-tree', '-r', '--name-only', ref]);
59
+ return stdout.trim().split('\n').filter(l => l && !l.startsWith('.nut/'));
60
+ }
61
+ // --- Instruction files ---
62
+ const FILENAME_TO_KIND = new Map(INSTRUCTION_FILE_KINDS.map(k => [`${k}.md`, k]));
63
+ const KIND_TO_FILENAME = new Map(INSTRUCTION_FILE_KINDS.map(k => [k, `${k}.md`]));
64
+ export async function getInstructionFiles(filter) {
65
+ const filterFilename = filter && filter !== 'all' ? KIND_TO_FILENAME.get(filter) : undefined;
66
+ const allPaths = await lsTree();
67
+ const wanted = allPaths.filter(p => {
68
+ const name = path.basename(p);
69
+ if (!FILENAME_TO_KIND.has(name))
70
+ return false;
71
+ if (filterFilename)
72
+ return name === filterFilename;
73
+ return true;
74
+ });
75
+ const entries = await Promise.all(wanted.map(async (filePath) => {
76
+ const { stdout } = await runGit(['show', `HEAD:${filePath}`]);
77
+ return { filePath, content: stdout };
78
+ }));
79
+ const result = {};
80
+ for (const { filePath, content } of entries) {
81
+ const dir = path.dirname(filePath);
82
+ const key = dir === '.' ? '.' : dir;
83
+ const kind = FILENAME_TO_KIND.get(path.basename(filePath));
84
+ if (!result[key])
85
+ result[key] = {};
86
+ result[key][kind] = content;
87
+ }
88
+ return result;
89
+ }
55
90
  // --- Status ---
56
91
  export async function getRepoStatus() {
57
92
  const { stdout } = await runGit(['status', '--porcelain=v1', '-b']);
@@ -2,6 +2,7 @@ import * as pty from 'node-pty';
2
2
  import { WebSocket } from 'ws';
3
3
  import path from 'path';
4
4
  import fs from 'fs';
5
+ import { randomBytes } from 'crypto';
5
6
  import { fileURLToPath } from 'url';
6
7
  import { createInitScript } from './context-helper.js';
7
8
  import { getShellPath, getShellArgs, prepareShellInit } from './shell-utils.js';
@@ -48,7 +49,7 @@ export class TerminalManager {
48
49
  ensureSpawnHelperPermissions();
49
50
  spawnHelperFixed = true;
50
51
  }
51
- const sessionId = `${taskId}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
52
+ const sessionId = `${taskId}-${Date.now()}-${randomBytes(9).toString('base64url')}`;
52
53
  // Get the project root directory
53
54
  let projectRoot;
54
55
  if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { homedir } from 'os';
2
3
  import { join, resolve as pathResolve, basename } from 'path';
3
4
  import { existsSync, readFileSync, promises as fs, createReadStream } from 'fs';
@@ -7,7 +8,7 @@ import { ZodError } from 'zod';
7
8
  import { streamText, tool, jsonSchema, stepCountIs } from 'ai';
8
9
  import { createAnthropic } from '@ai-sdk/anthropic';
9
10
  import { getLogsDir, listTasks, getTask, createTask, updateTask, deleteTask, } from '@lovelybunch/core';
10
- import { tasksFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool, resourcesTool } from '@lovelybunch/mcp';
11
+ import { tasksFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, roleContextTool, agentsContextTool, teamContextTool, memoryContextTool, resourcesTool, listConnectorsTool, connectorRequestTool, resolveConnectorUrl } from '@lovelybunch/mcp';
11
12
  import matter from 'gray-matter';
12
13
  import Fuse from 'fuse.js';
13
14
  import { FileStorageAdapter } from '../../../../lib/storage/file-storage.js';
@@ -59,6 +60,10 @@ export async function POST(c) {
59
60
  const systemPrompt = persona
60
61
  ? `${baseSystem}\n\nThe following persona is authoritative and overrides general guidance above. You must strictly follow it.\n\n${persona}`
61
62
  : baseSystem;
63
+ if (process.env.NODE_ENV === 'development') {
64
+ const sha256Prefix = createHash('sha256').update(systemPrompt).digest('hex').slice(0, 16);
65
+ console.log('AI system prompt debug:', { length: systemPrompt.length, sha256Prefix });
66
+ }
62
67
  // Build context messages from attached files
63
68
  const contextMessages = [];
64
69
  if (Array.isArray(attachedContextFiles)) {
@@ -120,27 +125,35 @@ export async function POST(c) {
120
125
  return JSON.stringify(result);
121
126
  },
122
127
  }),
123
- project_context: tool({
124
- description: projectContextTool.description,
125
- inputSchema: jsonSchema(projectContextTool.parameters),
128
+ role_context: tool({
129
+ description: roleContextTool.description,
130
+ inputSchema: jsonSchema(roleContextTool.parameters),
126
131
  execute: async (args) => {
127
- const result = await executeProjectContextToolDirect(args);
132
+ const result = await executeRoleContextToolDirect(args);
128
133
  return JSON.stringify(result);
129
134
  },
130
135
  }),
131
- architecture_context: tool({
132
- description: architectureContextTool.description,
133
- inputSchema: jsonSchema(architectureContextTool.parameters),
136
+ agents_context: tool({
137
+ description: agentsContextTool.description,
138
+ inputSchema: jsonSchema(agentsContextTool.parameters),
134
139
  execute: async (args) => {
135
- const result = await executeArchitectureContextToolDirect(args);
140
+ const result = await executeAgentsContextToolDirect(args);
136
141
  return JSON.stringify(result);
137
142
  },
138
143
  }),
139
- role_context: tool({
140
- description: roleContextTool.description,
141
- inputSchema: jsonSchema(roleContextTool.parameters),
144
+ team_context: tool({
145
+ description: teamContextTool.description,
146
+ inputSchema: jsonSchema(teamContextTool.parameters),
142
147
  execute: async (args) => {
143
- const result = await executeRoleContextToolDirect(args);
148
+ const result = await executeTeamContextToolDirect(args);
149
+ return JSON.stringify(result);
150
+ },
151
+ }),
152
+ memory_context: tool({
153
+ description: memoryContextTool.description,
154
+ inputSchema: jsonSchema(memoryContextTool.parameters),
155
+ execute: async (args) => {
156
+ const result = await executeMemoryContextToolDirect(args);
144
157
  return JSON.stringify(result);
145
158
  },
146
159
  }),
@@ -152,6 +165,22 @@ export async function POST(c) {
152
165
  return JSON.stringify(result);
153
166
  },
154
167
  }),
168
+ list_connectors: tool({
169
+ description: listConnectorsTool.description,
170
+ inputSchema: jsonSchema(listConnectorsTool.parameters),
171
+ execute: async () => {
172
+ const result = await executeListConnectorsToolDirect();
173
+ return JSON.stringify(result);
174
+ },
175
+ }),
176
+ connector_request: tool({
177
+ description: connectorRequestTool.description,
178
+ inputSchema: jsonSchema(connectorRequestTool.parameters),
179
+ execute: async (args) => {
180
+ const result = await executeConnectorRequestToolDirect(args);
181
+ return JSON.stringify(result);
182
+ },
183
+ }),
155
184
  };
156
185
  // Debug logging
157
186
  console.log('AI Request Debug:', {
@@ -521,12 +550,12 @@ async function executeEventsToolDirect(args) {
521
550
  return { success: false, error: error.message || 'Events tool execution failed' };
522
551
  }
523
552
  }
524
- // Project context tool - read/write the project definition document
525
- async function executeProjectContextToolDirect(args) {
553
+ // Role context tool - read/write the role definition document
554
+ async function executeRoleContextToolDirect(args) {
526
555
  const { operation, content, old_text, new_text } = args;
527
556
  try {
528
557
  const contextPath = getContextBasePath();
529
- const filePath = join(contextPath, 'project.md');
558
+ const filePath = join(contextPath, 'role.md');
530
559
  switch (operation) {
531
560
  case 'get': {
532
561
  try {
@@ -539,7 +568,7 @@ async function executeProjectContextToolDirect(args) {
539
568
  frontmatter: parsed.data,
540
569
  raw: fileContent
541
570
  },
542
- message: 'Retrieved project context'
571
+ message: 'Retrieved role context'
543
572
  };
544
573
  }
545
574
  catch (err) {
@@ -547,7 +576,7 @@ async function executeProjectContextToolDirect(args) {
547
576
  return {
548
577
  success: true,
549
578
  data: { content: '', frontmatter: {}, raw: '' },
550
- message: 'Project context document does not exist yet. You can create it with an update operation.'
579
+ message: 'Role context document does not exist yet. You can create it with an update operation.'
551
580
  };
552
581
  }
553
582
  throw err;
@@ -574,7 +603,7 @@ async function executeProjectContextToolDirect(args) {
574
603
  await fs.writeFile(filePath, newContent, 'utf-8');
575
604
  return {
576
605
  success: true,
577
- message: 'Appended content to project context document'
606
+ message: 'Appended content to role context document'
578
607
  };
579
608
  }
580
609
  case 'replace_section': {
@@ -588,7 +617,7 @@ async function executeProjectContextToolDirect(args) {
588
617
  }
589
618
  catch (err) {
590
619
  if (err.code === 'ENOENT') {
591
- return { success: false, error: 'Project context document does not exist. Use append or update to create it first.' };
620
+ return { success: false, error: 'Role context document does not exist. Use append or update to create it first.' };
592
621
  }
593
622
  throw err;
594
623
  }
@@ -598,7 +627,7 @@ async function executeProjectContextToolDirect(args) {
598
627
  success: false,
599
628
  error: 'Could not find the specified text in the document. The text may have been paraphrased or changed.',
600
629
  fallback_markdown: new_text,
601
- suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/project'
630
+ suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/role'
602
631
  };
603
632
  }
604
633
  // Replace the text
@@ -606,7 +635,7 @@ async function executeProjectContextToolDirect(args) {
606
635
  await fs.writeFile(filePath, updatedContent, 'utf-8');
607
636
  return {
608
637
  success: true,
609
- message: 'Replaced section in project context document'
638
+ message: 'Replaced section in role context document'
610
639
  };
611
640
  }
612
641
  case 'update': {
@@ -617,7 +646,7 @@ async function executeProjectContextToolDirect(args) {
617
646
  await fs.writeFile(filePath, content, 'utf-8');
618
647
  return {
619
648
  success: true,
620
- message: 'Updated project context document'
649
+ message: 'Updated role context document'
621
650
  };
622
651
  }
623
652
  default:
@@ -625,124 +654,28 @@ async function executeProjectContextToolDirect(args) {
625
654
  }
626
655
  }
627
656
  catch (error) {
628
- console.error('Error executing project context tool:', error);
629
- return { success: false, error: error.message || 'Project context tool execution failed' };
657
+ console.error('Error executing role context tool:', error);
658
+ return { success: false, error: error.message || 'Role context tool execution failed' };
630
659
  }
631
660
  }
632
- // Architecture context tool - read/write the architecture document
633
- async function executeArchitectureContextToolDirect(args) {
634
- const { operation, content, old_text, new_text } = args;
635
- try {
636
- const contextPath = getContextBasePath();
637
- const filePath = join(contextPath, 'architecture.md');
638
- switch (operation) {
639
- case 'get': {
640
- try {
641
- const fileContent = await fs.readFile(filePath, 'utf-8');
642
- const parsed = matter(fileContent);
643
- return {
644
- success: true,
645
- data: {
646
- content: parsed.content,
647
- frontmatter: parsed.data,
648
- raw: fileContent
649
- },
650
- message: 'Retrieved architecture context'
651
- };
652
- }
653
- catch (err) {
654
- if (err.code === 'ENOENT') {
655
- return {
656
- success: true,
657
- data: { content: '', frontmatter: {}, raw: '' },
658
- message: 'Architecture context document does not exist yet. You can create it with an update operation.'
659
- };
660
- }
661
- throw err;
662
- }
663
- }
664
- case 'append': {
665
- if (!content) {
666
- return { success: false, error: 'Content is required for append operation' };
667
- }
668
- await fs.mkdir(contextPath, { recursive: true });
669
- // Read existing content if file exists
670
- let existingContent = '';
671
- try {
672
- existingContent = await fs.readFile(filePath, 'utf-8');
673
- }
674
- catch (err) {
675
- if (err.code !== 'ENOENT')
676
- throw err;
677
- }
678
- // Append new content with a newline separator
679
- const newContent = existingContent
680
- ? existingContent.trimEnd() + '\n\n' + content
681
- : content;
682
- await fs.writeFile(filePath, newContent, 'utf-8');
683
- return {
684
- success: true,
685
- message: 'Appended content to architecture context document'
686
- };
687
- }
688
- case 'replace_section': {
689
- if (!old_text || !new_text) {
690
- return { success: false, error: 'Both old_text and new_text are required for replace_section operation' };
691
- }
692
- // Read existing content
693
- let existingContent = '';
694
- try {
695
- existingContent = await fs.readFile(filePath, 'utf-8');
696
- }
697
- catch (err) {
698
- if (err.code === 'ENOENT') {
699
- return { success: false, error: 'Architecture context document does not exist. Use append or update to create it first.' };
700
- }
701
- throw err;
702
- }
703
- // Check if old_text exists in the document
704
- if (!existingContent.includes(old_text)) {
705
- return {
706
- success: false,
707
- error: 'Could not find the specified text in the document. The text may have been paraphrased or changed.',
708
- fallback_markdown: new_text,
709
- suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/architecture'
710
- };
711
- }
712
- // Replace the text
713
- const updatedContent = existingContent.replace(old_text, new_text);
714
- await fs.writeFile(filePath, updatedContent, 'utf-8');
715
- return {
716
- success: true,
717
- message: 'Replaced section in architecture context document'
718
- };
719
- }
720
- case 'update': {
721
- if (!content) {
722
- return { success: false, error: 'Content is required for update operation' };
723
- }
724
- await fs.mkdir(contextPath, { recursive: true });
725
- await fs.writeFile(filePath, content, 'utf-8');
726
- return {
727
- success: true,
728
- message: 'Updated architecture context document'
729
- };
730
- }
731
- default:
732
- return { success: false, error: `Unknown operation: ${operation}. Use 'get', 'append', 'replace_section', or 'update'.` };
733
- }
734
- }
735
- catch (error) {
736
- console.error('Error executing architecture context tool:', error);
737
- return { success: false, error: error.message || 'Architecture context tool execution failed' };
738
- }
661
+ // Agents context tool - read/write the agents definition document
662
+ async function executeAgentsContextToolDirect(args) {
663
+ return executeGenericContextToolDirect('agents', args);
739
664
  }
740
- // Role context tool - read/write the role definition document
741
- async function executeRoleContextToolDirect(args) {
665
+ // Team context tool - read/write the team definition document
666
+ async function executeTeamContextToolDirect(args) {
667
+ return executeGenericContextToolDirect('team', args);
668
+ }
669
+ // Memory context tool - read/write the memory document
670
+ async function executeMemoryContextToolDirect(args) {
671
+ return executeGenericContextToolDirect('memory', args);
672
+ }
673
+ async function executeGenericContextToolDirect(contextType, args) {
742
674
  const { operation, content, old_text, new_text } = args;
675
+ const label = contextType.charAt(0).toUpperCase() + contextType.slice(1);
743
676
  try {
744
677
  const contextPath = getContextBasePath();
745
- const filePath = join(contextPath, 'role.md');
678
+ const filePath = join(contextPath, `${contextType}.md`);
746
679
  switch (operation) {
747
680
  case 'get': {
748
681
  try {
@@ -755,7 +688,7 @@ async function executeRoleContextToolDirect(args) {
755
688
  frontmatter: parsed.data,
756
689
  raw: fileContent
757
690
  },
758
- message: 'Retrieved role context'
691
+ message: `Retrieved ${contextType} context`
759
692
  };
760
693
  }
761
694
  catch (err) {
@@ -763,7 +696,7 @@ async function executeRoleContextToolDirect(args) {
763
696
  return {
764
697
  success: true,
765
698
  data: { content: '', frontmatter: {}, raw: '' },
766
- message: 'Role context document does not exist yet. You can create it with an update operation.'
699
+ message: `${label} context document does not exist yet. You can create it with an update operation.`
767
700
  };
768
701
  }
769
702
  throw err;
@@ -774,7 +707,6 @@ async function executeRoleContextToolDirect(args) {
774
707
  return { success: false, error: 'Content is required for append operation' };
775
708
  }
776
709
  await fs.mkdir(contextPath, { recursive: true });
777
- // Read existing content if file exists
778
710
  let existingContent = '';
779
711
  try {
780
712
  existingContent = await fs.readFile(filePath, 'utf-8');
@@ -783,46 +715,42 @@ async function executeRoleContextToolDirect(args) {
783
715
  if (err.code !== 'ENOENT')
784
716
  throw err;
785
717
  }
786
- // Append new content with a newline separator
787
718
  const newContent = existingContent
788
719
  ? existingContent.trimEnd() + '\n\n' + content
789
720
  : content;
790
721
  await fs.writeFile(filePath, newContent, 'utf-8');
791
722
  return {
792
723
  success: true,
793
- message: 'Appended content to role context document'
724
+ message: `Appended content to ${contextType} context document`
794
725
  };
795
726
  }
796
727
  case 'replace_section': {
797
728
  if (!old_text || !new_text) {
798
729
  return { success: false, error: 'Both old_text and new_text are required for replace_section operation' };
799
730
  }
800
- // Read existing content
801
731
  let existingContent = '';
802
732
  try {
803
733
  existingContent = await fs.readFile(filePath, 'utf-8');
804
734
  }
805
735
  catch (err) {
806
736
  if (err.code === 'ENOENT') {
807
- return { success: false, error: 'Role context document does not exist. Use append or update to create it first.' };
737
+ return { success: false, error: `${label} context document does not exist. Use append or update to create it first.` };
808
738
  }
809
739
  throw err;
810
740
  }
811
- // Check if old_text exists in the document
812
741
  if (!existingContent.includes(old_text)) {
813
742
  return {
814
743
  success: false,
815
744
  error: 'Could not find the specified text in the document. The text may have been paraphrased or changed.',
816
745
  fallback_markdown: new_text,
817
- suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/role'
746
+ suggestion: `Here is the replacement text. You can copy it and manually edit the document at /context/${contextType}`
818
747
  };
819
748
  }
820
- // Replace the text
821
749
  const updatedContent = existingContent.replace(old_text, new_text);
822
750
  await fs.writeFile(filePath, updatedContent, 'utf-8');
823
751
  return {
824
752
  success: true,
825
- message: 'Replaced section in role context document'
753
+ message: `Replaced section in ${contextType} context document`
826
754
  };
827
755
  }
828
756
  case 'update': {
@@ -833,7 +761,7 @@ async function executeRoleContextToolDirect(args) {
833
761
  await fs.writeFile(filePath, content, 'utf-8');
834
762
  return {
835
763
  success: true,
836
- message: 'Updated role context document'
764
+ message: `Updated ${contextType} context document`
837
765
  };
838
766
  }
839
767
  default:
@@ -841,8 +769,8 @@ async function executeRoleContextToolDirect(args) {
841
769
  }
842
770
  }
843
771
  catch (error) {
844
- console.error('Error executing role context tool:', error);
845
- return { success: false, error: error.message || 'Role context tool execution failed' };
772
+ console.error(`Error executing ${contextType} context tool:`, error);
773
+ return { success: false, error: error.message || `${label} context tool execution failed` };
846
774
  }
847
775
  }
848
776
  // ─── Resources Tool ─────────────────────────────────────────────────────────
@@ -1352,13 +1280,147 @@ function readRoleContext() {
1352
1280
  catch { }
1353
1281
  return null;
1354
1282
  }
1283
+ function readMemoryContext() {
1284
+ try {
1285
+ const contextPath = getContextBasePath();
1286
+ const memoryPath = join(contextPath, 'memory.md');
1287
+ if (existsSync(memoryPath)) {
1288
+ return readFileSync(memoryPath, 'utf-8');
1289
+ }
1290
+ }
1291
+ catch { }
1292
+ return null;
1293
+ }
1355
1294
  function getSystemPrompt() {
1356
1295
  const basePrompt = readSharedPrompt('coconut-assistant') ||
1357
1296
  `You are the Coconut 🥥 AI assistant for agent-native development workflows. You help developers with proposals, architecture decisions, planning, and execution.`;
1358
- // Add role context if it exists
1359
1297
  const roleContent = readRoleContext();
1298
+ const memoryContent = readMemoryContext();
1299
+ let result = basePrompt;
1360
1300
  if (roleContent) {
1361
- return `${basePrompt}\n\n## Your Role for This Instance\n\n${roleContent}`;
1301
+ result = `${result}\n\n## Your Role for This Instance\n\n${roleContent}`;
1302
+ }
1303
+ if (memoryContent) {
1304
+ result = `${result}\n\n## Memory for This Instance\n\n${memoryContent}`;
1305
+ }
1306
+ return result;
1307
+ }
1308
+ const COCONUT_HOST = 'https://app.coconut.dev';
1309
+ async function executeListConnectorsToolDirect() {
1310
+ const token = getGlobalApiKey('callbackToken');
1311
+ if (!token) {
1312
+ return {
1313
+ success: false,
1314
+ error: 'Callback token not configured. Set it via Settings > API Keys or PUT /api/v1/config?type=global with { "apiKeys": { "callbackToken": "cpt_xxx" } }'
1315
+ };
1316
+ }
1317
+ try {
1318
+ const res = await fetch(`${COCONUT_HOST}/api/connectors/accounts`, {
1319
+ headers: { 'X-Callback-Token': token }
1320
+ });
1321
+ if (!res.ok) {
1322
+ const body = await res.text().catch(() => '');
1323
+ return { success: false, error: `Control plane returned ${res.status}: ${body || res.statusText}` };
1324
+ }
1325
+ const data = await res.json();
1326
+ const accounts = Array.isArray(data) ? data : (data.accounts ?? data.data ?? []);
1327
+ return {
1328
+ success: true,
1329
+ data: accounts,
1330
+ count: accounts.length,
1331
+ message: `Found ${accounts.length} connected service${accounts.length === 1 ? '' : 's'}`
1332
+ };
1333
+ }
1334
+ catch (error) {
1335
+ return { success: false, error: error.message || 'Failed to list connectors' };
1336
+ }
1337
+ }
1338
+ async function executeConnectorRequestToolDirect(args) {
1339
+ const { app: appSlug, method, path: apiPath, body, headers: extraHeaders } = args;
1340
+ if (!appSlug || !method || !apiPath) {
1341
+ return { success: false, error: 'app, method, and path are required' };
1342
+ }
1343
+ const token = getGlobalApiKey('callbackToken');
1344
+ if (!token) {
1345
+ return {
1346
+ success: false,
1347
+ error: 'Callback token not configured. Set it via Settings > API Keys or PUT /api/v1/config?type=global with { "apiKeys": { "callbackToken": "cpt_xxx" } }'
1348
+ };
1349
+ }
1350
+ try {
1351
+ const accountsRes = await fetch(`${COCONUT_HOST}/api/connectors/accounts`, {
1352
+ headers: { 'X-Callback-Token': token }
1353
+ });
1354
+ if (!accountsRes.ok) {
1355
+ const errBody = await accountsRes.text().catch(() => '');
1356
+ return { success: false, error: `Failed to fetch accounts (${accountsRes.status}): ${errBody || accountsRes.statusText}` };
1357
+ }
1358
+ const accountsData = await accountsRes.json();
1359
+ const accounts = Array.isArray(accountsData) ? accountsData : (accountsData.accounts ?? accountsData.data ?? []);
1360
+ const account = accounts.find((a) => a.appSlug === appSlug || a.appName?.toLowerCase() === appSlug.toLowerCase());
1361
+ if (!account) {
1362
+ return {
1363
+ success: false,
1364
+ error: `No connected account found for "${appSlug}"`,
1365
+ available: accounts.map((a) => a.appSlug || a.appName)
1366
+ };
1367
+ }
1368
+ const url = resolveConnectorUrl(appSlug, apiPath);
1369
+ if (!url) {
1370
+ return {
1371
+ success: false,
1372
+ error: `Unknown app "${appSlug}" and path is not a full URL. Use a full URL (https://...) or a known app slug.`
1373
+ };
1374
+ }
1375
+ let parsedBody = undefined;
1376
+ if (body) {
1377
+ try {
1378
+ parsedBody = typeof body === 'string' ? JSON.parse(body) : body;
1379
+ }
1380
+ catch {
1381
+ return { success: false, error: 'Invalid JSON in body parameter' };
1382
+ }
1383
+ }
1384
+ let parsedHeaders = {};
1385
+ if (extraHeaders) {
1386
+ try {
1387
+ parsedHeaders = typeof extraHeaders === 'string' ? JSON.parse(extraHeaders) : extraHeaders;
1388
+ }
1389
+ catch {
1390
+ return { success: false, error: 'Invalid JSON in headers parameter' };
1391
+ }
1392
+ }
1393
+ const proxyPayload = {
1394
+ accountId: account.accountId,
1395
+ url,
1396
+ method: method.toUpperCase()
1397
+ };
1398
+ if (parsedBody !== undefined)
1399
+ proxyPayload.body = parsedBody;
1400
+ if (Object.keys(parsedHeaders).length > 0)
1401
+ proxyPayload.headers = parsedHeaders;
1402
+ const proxyRes = await fetch(`${COCONUT_HOST}/api/connectors/proxy`, {
1403
+ method: 'POST',
1404
+ headers: {
1405
+ 'Content-Type': 'application/json',
1406
+ 'X-Callback-Token': token
1407
+ },
1408
+ body: JSON.stringify(proxyPayload)
1409
+ });
1410
+ if (!proxyRes.ok) {
1411
+ const errBody = await proxyRes.text().catch(() => '');
1412
+ return { success: false, error: `Proxy request failed (${proxyRes.status}): ${errBody || proxyRes.statusText}` };
1413
+ }
1414
+ const responseData = await proxyRes.json().catch(async () => {
1415
+ return await proxyRes.text().catch(() => null);
1416
+ });
1417
+ return {
1418
+ success: true,
1419
+ data: responseData,
1420
+ message: `${method.toUpperCase()} ${apiPath} via ${appSlug}`
1421
+ };
1422
+ }
1423
+ catch (error) {
1424
+ return { success: false, error: error.message || 'Connector request failed' };
1362
1425
  }
1363
- return basePrompt;
1364
1426
  }