@agent-spaces/server 0.3.0 → 0.3.2

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 (187) hide show
  1. package/dist/adapters/claude-code-runtime/index.js +48 -6
  2. package/dist/adapters/claude-code-runtime/message-format.js +3 -3
  3. package/dist/adapters/git.js +140 -2
  4. package/dist/agents/agent-designer.js +334 -0
  5. package/dist/agents/agent-message-parts.js +8 -0
  6. package/dist/agents/commit-agent.js +9 -2
  7. package/dist/agents/issue-agent-runner.js +0 -1
  8. package/dist/agents/issue-task-controller.js +14 -4
  9. package/dist/app.js +51 -1
  10. package/dist/package.json +1 -1
  11. package/dist/routes/agent-sse.js +4 -1
  12. package/dist/routes/agent.js +15 -0
  13. package/dist/routes/channel.js +1 -1
  14. package/dist/routes/file.js +97 -1
  15. package/dist/routes/git.js +122 -1
  16. package/dist/routes/iframe-bookmarks.js +33 -0
  17. package/dist/routes/issue.js +12 -0
  18. package/dist/routes/speech-recognition.js +99 -0
  19. package/dist/services/agent.js +48 -5
  20. package/dist/services/builtin-tools.js +67 -1
  21. package/dist/services/channel.js +2 -0
  22. package/dist/services/command-process-manager.js +14 -1
  23. package/dist/services/file.js +86 -6
  24. package/dist/services/gitignore.js +65 -0
  25. package/dist/services/issue-retry.js +2 -1
  26. package/dist/services/issue.js +13 -4
  27. package/dist/services/notification-hub/bot-agent.js +9 -1
  28. package/dist/services/persistent-agent-context.js +155 -0
  29. package/dist/services/pty.js +47 -2
  30. package/dist/services/search.js +11 -3
  31. package/dist/services/speech-recognition/base.js +3 -0
  32. package/dist/services/speech-recognition/index.js +16 -0
  33. package/dist/services/speech-recognition/tencent.js +117 -0
  34. package/dist/storage/iframe-bookmarks-store.js +46 -0
  35. package/dist/storage/speech-recognition-store.js +61 -0
  36. package/dist/web/404.html +1 -1
  37. package/dist/web/__next.__PAGE__.txt +4 -4
  38. package/dist/web/__next._full.txt +31 -26
  39. package/dist/web/__next._head.txt +4 -4
  40. package/dist/web/__next._index.txt +17 -13
  41. package/dist/web/__next._tree.txt +2 -2
  42. package/dist/web/_next/static/chunks/00amm3dvu3po9.js +31 -0
  43. package/dist/web/_next/static/chunks/01hrib8z5bn-6.js +5 -0
  44. package/dist/web/_next/static/chunks/08nbjuc5i-ewu.js +3 -0
  45. package/dist/web/_next/static/chunks/0anuxr11yn5iv.js +2 -0
  46. package/dist/web/_next/static/chunks/0cxrev-hb8gkk.js +2 -0
  47. package/dist/web/_next/static/chunks/0egot8y~i-ge3.js +1 -0
  48. package/dist/web/_next/static/chunks/0e~0o6v_awcod.js +1 -0
  49. package/dist/web/_next/static/chunks/0f_dcx0nu5z7o.js +1 -0
  50. package/dist/web/_next/static/chunks/0fpf.rso9mhc0.js +1 -0
  51. package/dist/web/_next/static/chunks/0jc-4igj54z00.js +189 -0
  52. package/dist/web/_next/static/chunks/0plreh4_gkxf9.js +1 -0
  53. package/dist/web/_next/static/chunks/0u7_qd9hzqwac.js +1 -0
  54. package/dist/web/_next/static/chunks/0uo5x.bu.5u2-.css +1 -0
  55. package/dist/web/_next/static/chunks/0utxlfbckz4f5.js +1 -0
  56. package/dist/web/_next/static/chunks/0vesz93a98px4.js +1 -0
  57. package/dist/web/_next/static/chunks/0w1iudd1d46sw.js +8 -0
  58. package/dist/web/_next/static/chunks/0w3o.7vi.dlm6.js +1 -0
  59. package/dist/web/_next/static/chunks/0xtmr0y4vo-~p.js +1 -0
  60. package/dist/web/_next/static/chunks/11ozf700niy2z.js +1 -0
  61. package/dist/web/_next/static/chunks/{0jmsjlawtg6oa.js → 145gbwitjxamg.js} +1 -1
  62. package/dist/web/_next/static/chunks/{turbopack-0lxiiw.jhevml.js → turbopack-0kcwat8.leugk.js} +1 -1
  63. package/dist/web/_not-found/__next._full.txt +34 -30
  64. package/dist/web/_not-found/__next._head.txt +4 -4
  65. package/dist/web/_not-found/__next._index.txt +17 -13
  66. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  67. package/dist/web/_not-found/__next._not-found.txt +3 -3
  68. package/dist/web/_not-found/__next._tree.txt +2 -2
  69. package/dist/web/_not-found.html +1 -1
  70. package/dist/web/_not-found.txt +34 -30
  71. package/dist/web/index.html +1 -1
  72. package/dist/web/index.txt +31 -26
  73. package/dist/web/login/__next._full.txt +35 -29
  74. package/dist/web/login/__next._head.txt +4 -4
  75. package/dist/web/login/__next._index.txt +17 -13
  76. package/dist/web/login/__next._tree.txt +2 -2
  77. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  78. package/dist/web/login/__next.login.txt +3 -3
  79. package/dist/web/login.html +1 -1
  80. package/dist/web/login.txt +35 -29
  81. package/dist/web/settings/__next._full.txt +36 -34
  82. package/dist/web/settings/__next._head.txt +4 -4
  83. package/dist/web/settings/__next._index.txt +17 -13
  84. package/dist/web/settings/__next._tree.txt +2 -2
  85. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  86. package/dist/web/settings/__next.settings.txt +5 -5
  87. package/dist/web/settings/agents/__next._full.txt +38 -35
  88. package/dist/web/settings/agents/__next._head.txt +4 -4
  89. package/dist/web/settings/agents/__next._index.txt +17 -13
  90. package/dist/web/settings/agents/__next._tree.txt +2 -2
  91. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  92. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  93. package/dist/web/settings/agents/__next.settings.txt +5 -5
  94. package/dist/web/settings/agents.html +1 -1
  95. package/dist/web/settings/agents.txt +38 -35
  96. package/dist/web/settings/mcps/__next._full.txt +38 -35
  97. package/dist/web/settings/mcps/__next._head.txt +4 -4
  98. package/dist/web/settings/mcps/__next._index.txt +17 -13
  99. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  100. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  101. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  102. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  103. package/dist/web/settings/mcps.html +1 -1
  104. package/dist/web/settings/mcps.txt +38 -35
  105. package/dist/web/settings/models/__next._full.txt +38 -35
  106. package/dist/web/settings/models/__next._head.txt +4 -4
  107. package/dist/web/settings/models/__next._index.txt +17 -13
  108. package/dist/web/settings/models/__next._tree.txt +2 -2
  109. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  110. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  111. package/dist/web/settings/models/__next.settings.txt +5 -5
  112. package/dist/web/settings/models.html +1 -1
  113. package/dist/web/settings/models.txt +38 -35
  114. package/dist/web/settings/providers/__next._full.txt +38 -35
  115. package/dist/web/settings/providers/__next._head.txt +4 -4
  116. package/dist/web/settings/providers/__next._index.txt +17 -13
  117. package/dist/web/settings/providers/__next._tree.txt +2 -2
  118. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  119. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  120. package/dist/web/settings/providers/__next.settings.txt +5 -5
  121. package/dist/web/settings/providers.html +1 -1
  122. package/dist/web/settings/providers.txt +38 -35
  123. package/dist/web/settings/skills/__next._full.txt +38 -35
  124. package/dist/web/settings/skills/__next._head.txt +4 -4
  125. package/dist/web/settings/skills/__next._index.txt +17 -13
  126. package/dist/web/settings/skills/__next._tree.txt +2 -2
  127. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  128. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  129. package/dist/web/settings/skills/__next.settings.txt +5 -5
  130. package/dist/web/settings/skills.html +1 -1
  131. package/dist/web/settings/skills.txt +38 -35
  132. package/dist/web/settings.html +1 -1
  133. package/dist/web/settings.txt +36 -34
  134. package/dist/web/workflows/__next._full.txt +36 -31
  135. package/dist/web/workflows/__next._head.txt +4 -4
  136. package/dist/web/workflows/__next._index.txt +17 -13
  137. package/dist/web/workflows/__next._tree.txt +2 -2
  138. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  139. package/dist/web/workflows/__next.workflows.txt +3 -3
  140. package/dist/web/workflows.html +1 -1
  141. package/dist/web/workflows.txt +36 -31
  142. package/dist/web/workspace/_/__next._full.txt +39 -34
  143. package/dist/web/workspace/_/__next._head.txt +4 -4
  144. package/dist/web/workspace/_/__next._index.txt +17 -13
  145. package/dist/web/workspace/_/__next._tree.txt +2 -2
  146. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  147. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  148. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  149. package/dist/web/workspace/_.html +1 -1
  150. package/dist/web/workspace/_.txt +39 -34
  151. package/dist/web/workspaces/__next._full.txt +35 -30
  152. package/dist/web/workspaces/__next._head.txt +4 -4
  153. package/dist/web/workspaces/__next._index.txt +17 -13
  154. package/dist/web/workspaces/__next._tree.txt +2 -2
  155. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  156. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  157. package/dist/web/workspaces.html +1 -1
  158. package/dist/web/workspaces.txt +35 -30
  159. package/dist/ws/agent-prompt.js +48 -18
  160. package/dist/ws/agent-runner.js +10 -3
  161. package/dist/ws/message-parts.js +8 -0
  162. package/dist/ws/terminal-handler.js +1 -1
  163. package/package.json +1 -1
  164. package/public/avatars/1778953228323-5je54x-user.jpg +0 -0
  165. package/public/avatars/1778954057880-g01q6y-user.jpg +0 -0
  166. package/dist/web/_next/static/chunks/002-dxhags4dj.js +0 -2
  167. package/dist/web/_next/static/chunks/00fu7sjtftd3f.js +0 -1
  168. package/dist/web/_next/static/chunks/00s_1ft2-qz0r.js +0 -1
  169. package/dist/web/_next/static/chunks/02wkg1tjv~_t2.js +0 -1
  170. package/dist/web/_next/static/chunks/03jy~o.gx._e-.js +0 -3
  171. package/dist/web/_next/static/chunks/07bs5ds68tg38.js +0 -2
  172. package/dist/web/_next/static/chunks/0b1w9_ne4c2ez.js +0 -1
  173. package/dist/web/_next/static/chunks/0bzechrx76-ix.js +0 -1
  174. package/dist/web/_next/static/chunks/0csqa76ud3h4d.js +0 -1
  175. package/dist/web/_next/static/chunks/0fxdvo63ywaa~.js +0 -1
  176. package/dist/web/_next/static/chunks/0k5bmi-es-2nj.js +0 -1
  177. package/dist/web/_next/static/chunks/0pep4mkvt3.rh.js +0 -31
  178. package/dist/web/_next/static/chunks/0tqwdxb-8t_n1.js +0 -1
  179. package/dist/web/_next/static/chunks/0y5ipuqgqfrob.js +0 -8
  180. package/dist/web/_next/static/chunks/0yc41k980zabo.js +0 -1
  181. package/dist/web/_next/static/chunks/0zhhkzv4.0koq.js +0 -183
  182. package/dist/web/_next/static/chunks/0zl19l5tuoppw.js +0 -1
  183. package/dist/web/_next/static/chunks/102lz3yanx8i~.css +0 -1
  184. package/dist/web/_next/static/chunks/13wm9.vry~.lh.js +0 -5
  185. /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_buildManifest.js +0 -0
  186. /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_clientMiddlewareManifest.js +0 -0
  187. /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_ssgManifest.js +0 -0
@@ -36,6 +36,7 @@ export class ClaudeCodeRuntime {
36
36
  d(`starting | cwd=${cwd} model=${model ?? 'default'} targetModel=${this.config.model ?? 'default'} provider=${this.config.provider ?? 'default'} baseURL=${baseURL ?? 'default'} permissionMode=${permissionMode} maxTurns=${options?.maxTurns ?? '∞'} tools=claude_code mcpServers=${Object.keys(options?.mcpServers ?? {}).join(',') || '-'} skills=${skillNames.join(',') || '-'} configDir=${configDir ?? 'default'} sandboxDirs=${additionalDirectories.join(',') || '-'} claudeExecutable=${claudeExecutable ?? 'sdk-default'}`);
37
37
  d(`prompt: ${prompt.slice(0, 300)}${prompt.length > 300 ? '...' : ''}`);
38
38
  d(`sdk mcp servers | ${sdkMcpServerNames.join(',') || '-'}`);
39
+ const stderrLines = [];
39
40
  try {
40
41
  const queryOptions = {
41
42
  cwd,
@@ -48,7 +49,7 @@ export class ClaudeCodeRuntime {
48
49
  managedSettings: {
49
50
  strictPluginOnlyCustomization: ['mcp'],
50
51
  },
51
- settingSources: [],
52
+ settingSources: ['user', 'project', 'local'],
52
53
  strictMcpConfig: true,
53
54
  additionalDirectories,
54
55
  permissionMode,
@@ -57,8 +58,10 @@ export class ClaudeCodeRuntime {
57
58
  env: buildEnv(this.config, configDir, { baseURL, apiKey }),
58
59
  stderr: (data) => {
59
60
  const line = data.trim();
60
- if (line)
61
+ if (line) {
62
+ stderrLines.push(line);
61
63
  d(`stderr: ${line}`);
64
+ }
62
65
  },
63
66
  };
64
67
  this.activeQuery = query({ prompt, options: queryOptions });
@@ -69,6 +72,7 @@ export class ClaudeCodeRuntime {
69
72
  let usageLine = null;
70
73
  let usage;
71
74
  let costUsd;
75
+ let sawResult = false;
72
76
  const pendingAskUserQuestionToolIds = new Set();
73
77
  let waitingForUserAnswer = false;
74
78
  for await (const message of this.activeQuery) {
@@ -106,6 +110,7 @@ export class ClaudeCodeRuntime {
106
110
  options?.onEvent?.({ type: 'output', line });
107
111
  }
108
112
  if (message.type === 'result') {
113
+ sawResult = true;
109
114
  turns = message.num_turns;
110
115
  tokenCount = countUsageTokens(message.usage);
111
116
  usageLine = formatUsageLine(message.usage);
@@ -122,6 +127,22 @@ export class ClaudeCodeRuntime {
122
127
  }
123
128
  }
124
129
  const elapsed = Date.now() - startTime;
130
+ if (!sawResult) {
131
+ const runtimeError = extractRuntimeError([...stderrLines, ...output])
132
+ || 'Claude Code execution stopped before reporting a final result';
133
+ d(`failed ${elapsed}ms | turns=${turns} tokens=${tokenCount} | ${runtimeError}`);
134
+ appendUnique(output, stderrLines);
135
+ appendUnique(output, [runtimeError]);
136
+ return {
137
+ success: false,
138
+ summary: 'Claude Code execution failed',
139
+ artifacts: [],
140
+ error: runtimeError,
141
+ output,
142
+ usage,
143
+ costUsd,
144
+ };
145
+ }
125
146
  if (waitingForUserAnswer && (!error || isAskUserQuestionAutoResult(error))) {
126
147
  d(`waiting for user answer ${elapsed}ms | turns=${turns} tokens=${tokenCount}`);
127
148
  if (usageLine)
@@ -136,14 +157,16 @@ export class ClaudeCodeRuntime {
136
157
  };
137
158
  }
138
159
  if (error) {
139
- d(`failed ${elapsed}ms | turns=${turns} tokens=${tokenCount} | ${error}`);
160
+ const runtimeError = extractRuntimeError([error, ...stderrLines, ...output]) || error;
161
+ d(`failed ${elapsed}ms | turns=${turns} tokens=${tokenCount} | ${runtimeError}`);
162
+ appendUnique(output, stderrLines);
140
163
  if (usageLine)
141
164
  output.push(usageLine);
142
165
  return {
143
166
  success: false,
144
167
  summary: 'Claude Code execution failed',
145
168
  artifacts: [],
146
- error,
169
+ error: runtimeError,
147
170
  output,
148
171
  usage,
149
172
  costUsd,
@@ -169,10 +192,13 @@ export class ClaudeCodeRuntime {
169
192
  catch (err) {
170
193
  const elapsed = Date.now() - startTime;
171
194
  const message = err instanceof Error ? err.message : String(err);
172
- d(`failed ${elapsed}ms | ${message}`);
195
+ const runtimeError = extractRuntimeError([message, ...stderrLines, ...output]) || message;
196
+ d(`failed ${elapsed}ms | ${runtimeError}`);
173
197
  if (err instanceof Error && err.stack)
174
198
  console.error(err.stack);
175
- return { success: false, summary: 'Claude Code execution failed', artifacts: [], error: message, output };
199
+ appendUnique(output, stderrLines);
200
+ appendUnique(output, [runtimeError]);
201
+ return { success: false, summary: 'Claude Code execution failed', artifacts: [], error: runtimeError, output };
176
202
  }
177
203
  finally {
178
204
  this.activeQuery?.close();
@@ -195,4 +221,20 @@ function readTotalCostUsd(message) {
195
221
  const value = message.total_cost_usd;
196
222
  return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
197
223
  }
224
+ function extractRuntimeError(lines) {
225
+ const text = lines
226
+ .filter((line) => typeof line === 'string' && line.trim().length > 0)
227
+ .join('\n');
228
+ if (!text)
229
+ return undefined;
230
+ const match = text.match(/(?:API Error|Request rejected|Too Many Requests|rate limit|overloaded|429)[^\n]*/i);
231
+ return match?.[0]?.trim();
232
+ }
233
+ function appendUnique(target, lines) {
234
+ for (const line of lines) {
235
+ if (!line || target.includes(line))
236
+ continue;
237
+ target.push(line);
238
+ }
239
+ }
198
240
  //# sourceMappingURL=index.js.map
@@ -5,9 +5,9 @@ export function formatMessage(message) {
5
5
  case 'result':
6
6
  return message.subtype === 'success' ? message.result : message.errors.join('\n');
7
7
  case 'system':
8
- if (message.subtype === 'init') {
9
- return `Claude Code initialized with ${message.model}`;
10
- }
8
+ // if (message.subtype === 'init') {
9
+ // return `Claude Code initialized with ${message.model}`;
10
+ // }
11
11
  if (message.subtype === 'task_started') {
12
12
  return message.description;
13
13
  }
@@ -50,6 +50,8 @@ function mapStatus(raw) {
50
50
  ahead: raw.ahead,
51
51
  behind: raw.behind,
52
52
  clean: raw.isClean(),
53
+ insertions: 0,
54
+ deletions: 0,
53
55
  };
54
56
  }
55
57
  function mapStatusCode(code) {
@@ -67,7 +69,39 @@ export async function gitStatus(workspaceId) {
67
69
  throw new Error('Workspace not found');
68
70
  const git = getGit(ws);
69
71
  const raw = await git.status();
70
- return mapStatus(raw);
72
+ const result = mapStatus(raw);
73
+ if (!result.clean) {
74
+ try {
75
+ // Tracked changes: modified/deleted/renamed
76
+ const trackedStat = await git.diff(['--shortstat']);
77
+ let insertions = 0, deletions = 0;
78
+ const mIns = trackedStat.match(/(\d+) insertion/);
79
+ const mDel = trackedStat.match(/(\d+) deletion/);
80
+ if (mIns)
81
+ insertions += parseInt(mIns[1]);
82
+ if (mDel)
83
+ deletions += parseInt(mDel[1]);
84
+ // Staged new files: --cached --shortstat
85
+ const stagedStat = await git.diff(['--cached', '--shortstat']);
86
+ const sIns = stagedStat.match(/(\d+) insertion/);
87
+ const sDel = stagedStat.match(/(\d+) deletion/);
88
+ if (sIns)
89
+ insertions += parseInt(sIns[1]);
90
+ if (sDel)
91
+ deletions += parseInt(sDel[1]);
92
+ result.insertions = insertions;
93
+ result.deletions = deletions;
94
+ }
95
+ catch {
96
+ result.insertions = 0;
97
+ result.deletions = 0;
98
+ }
99
+ }
100
+ else {
101
+ result.insertions = 0;
102
+ result.deletions = 0;
103
+ }
104
+ return result;
71
105
  }
72
106
  export async function gitDiff(workspaceId, filePath) {
73
107
  const ws = getWorkspace(workspaceId);
@@ -148,7 +182,14 @@ export async function gitDiscard(workspaceId, filePath) {
148
182
  if (!ws)
149
183
  throw new Error('Workspace not found');
150
184
  const git = getGit(ws);
151
- await git.checkout(['--', filePath]);
185
+ const status = await git.status();
186
+ const isUntracked = status.not_added.includes(filePath);
187
+ if (isUntracked) {
188
+ await git.clean('f', ['-d', '--', filePath]);
189
+ }
190
+ else {
191
+ await git.checkout(['--', filePath]);
192
+ }
152
193
  }
153
194
  export async function gitDiscardAll(workspaceId) {
154
195
  const ws = getWorkspace(workspaceId);
@@ -247,6 +288,103 @@ export async function gitAddRemote(workspaceId, name, url) {
247
288
  await git.addRemote(name, url);
248
289
  }
249
290
  }
291
+ export async function gitCheckoutDetached(workspaceId, commitHash) {
292
+ const ws = getWorkspace(workspaceId);
293
+ if (!ws)
294
+ throw new Error('Workspace not found');
295
+ const git = getGit(ws);
296
+ await git.checkout([commitHash]);
297
+ }
298
+ export async function gitCherryPick(workspaceId, commitHash) {
299
+ const ws = getWorkspace(workspaceId);
300
+ if (!ws)
301
+ throw new Error('Workspace not found');
302
+ const git = getGit(ws);
303
+ await git.raw(['cherry-pick', commitHash]);
304
+ }
305
+ export async function gitCreateBranch(workspaceId, name, startPoint) {
306
+ const ws = getWorkspace(workspaceId);
307
+ if (!ws)
308
+ throw new Error('Workspace not found');
309
+ const git = getGit(ws);
310
+ const args = startPoint ? [name, startPoint] : [name];
311
+ await git.branch(args);
312
+ }
313
+ export async function gitDeleteBranch(workspaceId, name, force = false) {
314
+ const ws = getWorkspace(workspaceId);
315
+ if (!ws)
316
+ throw new Error('Workspace not found');
317
+ const git = getGit(ws);
318
+ await git.branch([force ? '-D' : '-d', name]);
319
+ }
320
+ export async function gitCreateTag(workspaceId, name, commitHash) {
321
+ const ws = getWorkspace(workspaceId);
322
+ if (!ws)
323
+ throw new Error('Workspace not found');
324
+ const git = getGit(ws);
325
+ const args = [name];
326
+ if (commitHash)
327
+ args.push(commitHash);
328
+ await git.addTag(args.join(' '));
329
+ }
330
+ export async function gitCommitDiff(workspaceId, commitHash) {
331
+ const ws = getWorkspace(workspaceId);
332
+ if (!ws)
333
+ throw new Error('Workspace not found');
334
+ const git = getGit(ws);
335
+ const diff = await git.diff([`${commitHash}^`, commitHash]);
336
+ const nameOnlyRaw = await git.diff(['--name-only', `${commitHash}^`, commitHash]);
337
+ const files = nameOnlyRaw.split('\n').filter(Boolean);
338
+ const diffs = [];
339
+ for (const f of files) {
340
+ const oldContent = await git.show([`${commitHash}^:${f}`]).catch(() => '');
341
+ const newContent = await git.show([`${commitHash}:${f}`]).catch(() => '');
342
+ diffs.push({ path: f, oldContent, newContent, isBinary: false, isNew: !oldContent, isDeleted: !newContent });
343
+ }
344
+ return diffs;
345
+ }
346
+ export async function gitGetRemoteUrl(workspaceId) {
347
+ const ws = getWorkspace(workspaceId);
348
+ if (!ws)
349
+ throw new Error('Workspace not found');
350
+ const git = getGit(ws);
351
+ const remotes = await git.getRemotes(true);
352
+ return remotes.find(r => r.name === 'origin')?.refs?.push ?? null;
353
+ }
354
+ export async function gitMergeBase(workspaceId) {
355
+ const ws = getWorkspace(workspaceId);
356
+ if (!ws)
357
+ throw new Error('Workspace not found');
358
+ const git = getGit(ws);
359
+ const result = await git.raw(['merge-base', 'HEAD', 'origin/HEAD']).catch(() => '');
360
+ return result.trim();
361
+ }
362
+ export async function gitGetConfig(scope, workspaceId) {
363
+ const scopeFlag = scope === 'global' ? '--global' : '--local';
364
+ const git = workspaceId ? getGit(getWorkspace(workspaceId)) : simpleGit();
365
+ const get = (key) => git.raw(['config', scopeFlag, '--get', key]).then(v => v.trim()).catch(() => '');
366
+ const [name, email, proxy] = await Promise.all([
367
+ get('user.name'),
368
+ get('user.email'),
369
+ get('http.proxy'),
370
+ ]);
371
+ return { name, email, proxy };
372
+ }
373
+ export async function gitSetConfig(scope, config, workspaceId) {
374
+ const scopeFlag = scope === 'global' ? '--global' : '--local';
375
+ const git = workspaceId ? getGit(getWorkspace(workspaceId)) : simpleGit();
376
+ const set = (key, value) => {
377
+ if (value)
378
+ return git.raw(['config', scopeFlag, key, value]);
379
+ return git.raw(['config', scopeFlag, '--unset', key]).catch(() => { });
380
+ };
381
+ await Promise.all([
382
+ config.name !== undefined ? set('user.name', config.name) : Promise.resolve(),
383
+ config.email !== undefined ? set('user.email', config.email) : Promise.resolve(),
384
+ config.proxy !== undefined ? set('http.proxy', config.proxy) : Promise.resolve(),
385
+ config.proxy !== undefined ? set('https.proxy', config.proxy) : Promise.resolve(),
386
+ ]);
387
+ }
250
388
  export async function gitInit(workspaceId) {
251
389
  const ws = getWorkspace(workspaceId);
252
390
  if (!ws)
@@ -0,0 +1,334 @@
1
+ import { AGENT_GENERATOR_PRESET_ID, readAgentTemplate } from '../services/agent.js';
2
+ const SYSTEM_PROMPT = `You generate Agent Spaces agent presets.
3
+ Return only a valid JSON object with this exact schema:
4
+ {
5
+ "name": "short agent name",
6
+ "description": "one sentence description",
7
+ "systemPrompt": "markdown system prompt"
8
+ }
9
+
10
+ Rules:
11
+ - Do not wrap the JSON in markdown fences.
12
+ - name must be concise and suitable for a UI label.
13
+ - description must explain the agent's responsibility.
14
+ - systemPrompt must be valid Markdown and include role, responsibilities, workflow, constraints, and output expectations.
15
+ - Keep the systemPrompt actionable and specific to the user's request.`;
16
+ export async function generateAgentDesign(userPrompt) {
17
+ const prompt = userPrompt.trim();
18
+ if (!prompt)
19
+ throw new Error('prompt is required');
20
+ const config = resolveModelConfig();
21
+ if (!config) {
22
+ throw new Error(`Configure model settings for ${AGENT_GENERATOR_PRESET_ID} before generating agents.`);
23
+ }
24
+ console.info('[agent-designer] generating agent design', {
25
+ agentId: AGENT_GENERATOR_PRESET_ID,
26
+ provider: config.modelProvider ?? inferProvider(config.apiBase),
27
+ modelId: config.modelId,
28
+ apiBase: maskUrl(config.apiBase),
29
+ promptLength: prompt.length,
30
+ });
31
+ const content = await requestDesign(config, prompt);
32
+ console.info('[agent-designer] model text extracted', {
33
+ length: content.length,
34
+ preview: content.slice(0, 500),
35
+ });
36
+ return normalizeDesign(parseJsonObject(content));
37
+ }
38
+ function resolveModelConfig() {
39
+ const preset = readAgentTemplate(AGENT_GENERATOR_PRESET_ID);
40
+ if (preset?.apiBase && preset.apiKey && preset.modelId) {
41
+ return {
42
+ modelProvider: preset.modelProvider,
43
+ modelId: preset.modelId,
44
+ apiBase: preset.apiBase,
45
+ apiKey: preset.apiKey,
46
+ systemPrompt: preset.systemPrompt,
47
+ temperature: preset.temperature,
48
+ maxTokens: preset.maxTokens,
49
+ };
50
+ }
51
+ return null;
52
+ }
53
+ async function requestDesign(config, userPrompt) {
54
+ const provider = config.modelProvider ?? inferProvider(config.apiBase);
55
+ if (provider === 'anthropic-messages')
56
+ return requestAnthropic(config, userPrompt);
57
+ if (provider === 'gemini-generate-content')
58
+ return requestGemini(config, userPrompt);
59
+ return requestOpenAICompatible(config, userPrompt, provider === 'openai-responses' || provider === 'openai-responses-to-anthropic-messages');
60
+ }
61
+ async function requestOpenAICompatible(config, userPrompt, useResponsesApi) {
62
+ const url = joinUrl(config.apiBase, useResponsesApi ? '/responses' : '/chat/completions');
63
+ const response = await fetch(url, {
64
+ method: 'POST',
65
+ headers: {
66
+ Authorization: `Bearer ${config.apiKey}`,
67
+ 'Content-Type': 'application/json',
68
+ },
69
+ body: JSON.stringify(useResponsesApi
70
+ ? {
71
+ model: config.modelId,
72
+ input: `${buildSystemPrompt(config)}\n\nUser request:\n${userPrompt}`,
73
+ temperature: config.temperature ?? 0.2,
74
+ max_output_tokens: config.maxTokens,
75
+ }
76
+ : {
77
+ model: config.modelId,
78
+ messages: [
79
+ { role: 'system', content: buildSystemPrompt(config) },
80
+ { role: 'user', content: userPrompt },
81
+ ],
82
+ temperature: config.temperature ?? 0.2,
83
+ max_tokens: config.maxTokens,
84
+ }),
85
+ });
86
+ const body = await readResponseBody(response);
87
+ if (!response.ok || body.error)
88
+ throw new Error(body.error || `Agent design generation failed with status ${response.status}`);
89
+ return body.text;
90
+ }
91
+ async function requestAnthropic(config, userPrompt) {
92
+ const response = await fetch(getAnthropicMessagesUrl(config.apiBase), {
93
+ method: 'POST',
94
+ headers: {
95
+ 'x-api-key': config.apiKey,
96
+ 'anthropic-version': '2023-06-01',
97
+ 'Content-Type': 'application/json',
98
+ },
99
+ body: JSON.stringify({
100
+ model: config.modelId,
101
+ system: buildSystemPrompt(config),
102
+ messages: [{ role: 'user', content: userPrompt }],
103
+ max_tokens: config.maxTokens ?? 4096,
104
+ temperature: config.temperature ?? 0.2,
105
+ }),
106
+ });
107
+ const body = await readResponseBody(response);
108
+ if (!response.ok || body.error)
109
+ throw new Error(body.error || `Agent design generation failed with status ${response.status}`);
110
+ return body.text;
111
+ }
112
+ async function requestGemini(config, userPrompt) {
113
+ const response = await fetch(joinUrl(config.apiBase, `/models/${encodeURIComponent(config.modelId)}:generateContent`), {
114
+ method: 'POST',
115
+ headers: {
116
+ 'x-goog-api-key': config.apiKey,
117
+ 'Content-Type': 'application/json',
118
+ },
119
+ body: JSON.stringify({
120
+ systemInstruction: { parts: [{ text: buildSystemPrompt(config) }] },
121
+ contents: [{ role: 'user', parts: [{ text: userPrompt }] }],
122
+ generationConfig: {
123
+ temperature: config.temperature ?? 0.2,
124
+ maxOutputTokens: config.maxTokens,
125
+ },
126
+ }),
127
+ });
128
+ const body = await readResponseBody(response);
129
+ if (!response.ok || body.error)
130
+ throw new Error(body.error || `Agent design generation failed with status ${response.status}`);
131
+ return body.text;
132
+ }
133
+ function buildSystemPrompt(config) {
134
+ const custom = config.systemPrompt?.trim();
135
+ if (!custom)
136
+ return SYSTEM_PROMPT;
137
+ return `${custom}\n\n${SYSTEM_PROMPT}`;
138
+ }
139
+ async function readResponseBody(response) {
140
+ const raw = await response.text();
141
+ if (!raw)
142
+ return { text: '' };
143
+ try {
144
+ const json = JSON.parse(raw);
145
+ console.info('[agent-designer] provider response received', {
146
+ status: response.status,
147
+ keys: Object.keys(json),
148
+ preview: raw.slice(0, 800),
149
+ });
150
+ if (isAgentDesignJson(json))
151
+ return { text: JSON.stringify(json) };
152
+ return {
153
+ text: extractText(json),
154
+ error: extractError(json),
155
+ };
156
+ }
157
+ catch {
158
+ console.info('[agent-designer] provider raw text received', {
159
+ status: response.status,
160
+ preview: raw.slice(0, 800),
161
+ });
162
+ return { text: raw };
163
+ }
164
+ }
165
+ function extractText(json) {
166
+ const outputText = json.output_text;
167
+ if (typeof outputText === 'string')
168
+ return outputText;
169
+ const output = Array.isArray(json.output) ? json.output : [];
170
+ const responseOutputText = output
171
+ .flatMap((item) => Array.isArray(item.content) ? item.content : [])
172
+ .map((part) => {
173
+ const record = part;
174
+ return typeof record.text === 'string' ? record.text : '';
175
+ })
176
+ .filter(Boolean)
177
+ .join('\n');
178
+ if (responseOutputText)
179
+ return responseOutputText;
180
+ const choices = Array.isArray(json.choices) ? json.choices : [];
181
+ const firstChoice = choices[0];
182
+ if (typeof firstChoice?.text === 'string')
183
+ return firstChoice.text;
184
+ const message = firstChoice?.message;
185
+ if (typeof message?.content === 'string')
186
+ return message.content;
187
+ if (Array.isArray(message?.content)) {
188
+ const messageText = message.content
189
+ .map((part) => typeof part.text === 'string' ? part.text : '')
190
+ .filter(Boolean)
191
+ .join('\n');
192
+ if (messageText)
193
+ return messageText;
194
+ }
195
+ const content = Array.isArray(json.content) ? json.content : [];
196
+ const anthropicText = content
197
+ .map((part) => typeof part.text === 'string' ? part.text : '')
198
+ .filter(Boolean)
199
+ .join('\n');
200
+ if (anthropicText)
201
+ return anthropicText;
202
+ const candidates = Array.isArray(json.candidates) ? json.candidates : [];
203
+ const firstCandidate = candidates[0];
204
+ const parts = (firstCandidate?.content?.parts ?? []);
205
+ return parts
206
+ .map((part) => typeof part.text === 'string' ? part.text : '')
207
+ .filter(Boolean)
208
+ .join('\n');
209
+ }
210
+ function isAgentDesignJson(json) {
211
+ return typeof json.name === 'string'
212
+ && typeof json.description === 'string'
213
+ && typeof json.systemPrompt === 'string';
214
+ }
215
+ function extractError(json) {
216
+ if (json.success === false) {
217
+ return typeof json.msg === 'string' ? json.msg : 'Provider returned success=false';
218
+ }
219
+ const error = json.error;
220
+ if (typeof error === 'string')
221
+ return error;
222
+ if (error && typeof error === 'object') {
223
+ const message = error.message;
224
+ if (typeof message === 'string')
225
+ return message;
226
+ }
227
+ return typeof json.message === 'string' ? json.message : undefined;
228
+ }
229
+ function parseJsonObject(text) {
230
+ const trimmed = text.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '');
231
+ try {
232
+ return JSON.parse(trimmed);
233
+ }
234
+ catch {
235
+ const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i)?.[1]?.trim();
236
+ if (fenced)
237
+ return JSON.parse(fenced);
238
+ const candidate = findFirstJsonObject(trimmed);
239
+ if (candidate)
240
+ return JSON.parse(candidate);
241
+ throw new Error('Model did not return valid JSON.');
242
+ }
243
+ }
244
+ function findFirstJsonObject(text) {
245
+ const starts = [];
246
+ for (let index = 0; index < text.length; index += 1) {
247
+ if (text[index] === '{')
248
+ starts.push(index);
249
+ }
250
+ for (const start of starts) {
251
+ let depth = 0;
252
+ let inString = false;
253
+ let escaped = false;
254
+ for (let index = start; index < text.length; index += 1) {
255
+ const char = text[index];
256
+ if (escaped) {
257
+ escaped = false;
258
+ continue;
259
+ }
260
+ if (char === '\\') {
261
+ escaped = true;
262
+ continue;
263
+ }
264
+ if (char === '"') {
265
+ inString = !inString;
266
+ continue;
267
+ }
268
+ if (inString)
269
+ continue;
270
+ if (char === '{')
271
+ depth += 1;
272
+ if (char === '}')
273
+ depth -= 1;
274
+ if (depth === 0) {
275
+ const candidate = text.slice(start, index + 1);
276
+ try {
277
+ JSON.parse(candidate);
278
+ return candidate;
279
+ }
280
+ catch {
281
+ break;
282
+ }
283
+ }
284
+ }
285
+ }
286
+ return null;
287
+ }
288
+ function normalizeDesign(value) {
289
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
290
+ throw new Error('Generated agent design must be a JSON object.');
291
+ }
292
+ const data = value;
293
+ const name = typeof data.name === 'string' ? data.name.trim() : '';
294
+ const description = typeof data.description === 'string' ? data.description.trim() : '';
295
+ const systemPrompt = typeof data.systemPrompt === 'string' ? data.systemPrompt.trim() : '';
296
+ if (!name || !description || !systemPrompt) {
297
+ throw new Error('Generated JSON must include name, description, and systemPrompt.');
298
+ }
299
+ return { name, description, systemPrompt };
300
+ }
301
+ function inferProvider(apiBase) {
302
+ if (apiBase?.includes('anthropic.com'))
303
+ return 'anthropic-messages';
304
+ if (apiBase?.includes('generativelanguage.googleapis.com'))
305
+ return 'gemini-generate-content';
306
+ return 'openai-chat-completions';
307
+ }
308
+ function joinUrl(base, path) {
309
+ return `${base.replace(/\/+$/, '')}${path}`;
310
+ }
311
+ function getAnthropicMessagesUrl(apiBase) {
312
+ try {
313
+ const url = new URL(apiBase);
314
+ if (url.pathname.endsWith('/messages'))
315
+ return apiBase;
316
+ if (url.hostname === 'api.anthropic.com') {
317
+ return joinUrl(apiBase, '/messages');
318
+ }
319
+ return joinUrl(apiBase, '/v1/messages');
320
+ }
321
+ catch {
322
+ return apiBase;
323
+ }
324
+ }
325
+ function maskUrl(value) {
326
+ try {
327
+ const url = new URL(value);
328
+ return `${url.origin}${url.pathname}`;
329
+ }
330
+ catch {
331
+ return value.slice(0, 120);
332
+ }
333
+ }
334
+ //# sourceMappingURL=agent-designer.js.map
@@ -134,6 +134,8 @@ function normalizeOutputLines(output) {
134
134
  .filter((line) => line.trim());
135
135
  const seenInitLines = new Set();
136
136
  return lines.filter((line) => {
137
+ if (isIgnorableToolProgressLine(line))
138
+ return false;
137
139
  if (!/^Claude Code initialized\b/i.test(line))
138
140
  return true;
139
141
  if (seenInitLines.has(line))
@@ -429,6 +431,12 @@ function summarizeMessageTitle(text) {
429
431
  function isToolLikeLine(line) {
430
432
  return /^(Using|Tool:|Read|Write|Edit|MultiEdit|Bash|Search|Grep|Glob|Todo|Task|Web|Fetch|Claude Code initialized|Codex initialized|.+ running \(\d+s\))/i.test(line.trim());
431
433
  }
434
+ function isIgnorableToolProgressLine(line) {
435
+ const trimmed = line.trim();
436
+ return /^Claude$/i.test(trimmed)
437
+ || /^(Reading|Searching)(\.{1,3}|…)?$/i.test(trimmed)
438
+ || /^(Read|Search|Grep|Glob|SemanticSearch|WebSearch)\s+running\s+\(\d+s\)$/i.test(trimmed);
439
+ }
432
440
  function isFinalAnswerLine(line) {
433
441
  return !isToolLikeLine(line) && !/^(\[.*\]|Agent runtime configuration:|Conversation history:)/.test(line.trim());
434
442
  }
@@ -2,6 +2,7 @@ import * as agentService from '../services/agent.js';
2
2
  import { createAgentRuntime } from '../adapters/agent-runtime.js';
3
3
  import { getWorkspace } from '../storage/workspace-store.js';
4
4
  import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
5
+ import { prependPersistentAgentContext } from '../services/persistent-agent-context.js';
5
6
  import { simpleGit } from 'simple-git';
6
7
  const DEFAULT_SYSTEM_PROMPT = [
7
8
  'You are a git commit message generator.',
@@ -36,13 +37,19 @@ export async function runCommitAgent(workspaceId) {
36
37
  const workingDir = agentService.resolveWorkingDir(workspaceId, commitAgent);
37
38
  const systemPrompt = commitAgent.systemPrompt?.trim() || DEFAULT_SYSTEM_PROMPT;
38
39
  const truncatedDiff = diff.length > 8000 ? diff.substring(0, 8000) + '\n... (truncated)' : diff;
39
- const result = await runtime.execute([
40
+ const result = await runtime.execute(prependPersistentAgentContext([
40
41
  'Generate exactly one commit message for these changes.',
41
42
  'Do not inspect files or run commands. Use only this diff.',
42
43
  'Output only the commit message.',
43
44
  '',
44
45
  truncatedDiff,
45
- ].join('\n'), workingDir, {
46
+ ].join('\n'), {
47
+ workspaceId,
48
+ workingDir,
49
+ boundDirs: ws.boundDirs,
50
+ includeWorkspacePrompt: false,
51
+ excludeNativeClaudeMd: commitAgent.runtimeKind === 'claude-code',
52
+ }), workingDir, {
46
53
  maxTurns: 1,
47
54
  systemPrompt,
48
55
  });