@pixelbyte-software/pixcode 1.52.4 → 1.53.1

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 (98) hide show
  1. package/README.md +9 -10
  2. package/README.tr.md +1 -1
  3. package/dist/api-automation.html +1 -1
  4. package/dist/assets/index-BO0lz8ZD.js +816 -0
  5. package/dist/assets/index-DnjqWG8B.css +32 -0
  6. package/dist/docs.html +10 -10
  7. package/dist/features.html +3 -3
  8. package/dist/index.html +2 -2
  9. package/dist/landing.html +16 -16
  10. package/dist/llms-full.txt +4 -4
  11. package/dist/llms.txt +3 -3
  12. package/dist/openapi.yaml +6 -6
  13. package/dist-server/server/index.js +8 -125
  14. package/dist-server/server/index.js.map +1 -1
  15. package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js +1 -1
  16. package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js.map +1 -1
  17. package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js +1 -1
  18. package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js.map +1 -1
  19. package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js +1 -1
  20. package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js.map +1 -1
  21. package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js +1 -1
  22. package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js.map +1 -1
  23. package/dist-server/server/modules/orchestration/a2a/adapters/json-event.adapter.js +1 -1
  24. package/dist-server/server/modules/orchestration/a2a/adapters/json-event.adapter.js.map +1 -1
  25. package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js +1 -1
  26. package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js.map +1 -1
  27. package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js +1 -1
  28. package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js.map +1 -1
  29. package/dist-server/server/modules/orchestration/a2a/agent-card.js +4 -4
  30. package/dist-server/server/modules/orchestration/a2a/agent-card.js.map +1 -1
  31. package/dist-server/server/modules/orchestration/a2a/routes.js +2 -5
  32. package/dist-server/server/modules/orchestration/a2a/routes.js.map +1 -1
  33. package/dist-server/server/modules/orchestration/a2a/task-dispatcher.js +295 -0
  34. package/dist-server/server/modules/orchestration/a2a/task-dispatcher.js.map +1 -0
  35. package/dist-server/server/modules/orchestration/index.js +0 -2
  36. package/dist-server/server/modules/orchestration/index.js.map +1 -1
  37. package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js +2 -2
  38. package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js.map +1 -1
  39. package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js +12 -0
  40. package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js.map +1 -1
  41. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +19 -26
  42. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -1
  43. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +47 -58
  44. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
  45. package/dist-server/server/modules/orchestration/workflows/workflow-trace.js +2 -2
  46. package/dist-server/server/modules/orchestration/workflows/workflow-trace.js.map +1 -1
  47. package/dist-server/server/routes/git.js +79 -21
  48. package/dist-server/server/routes/git.js.map +1 -1
  49. package/dist-server/server/services/public-api-manifest.js +0 -9
  50. package/dist-server/server/services/public-api-manifest.js.map +1 -1
  51. package/package.json +1 -1
  52. package/server/index.js +7 -134
  53. package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +1 -1
  54. package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +1 -1
  55. package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +1 -1
  56. package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +1 -1
  57. package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +1 -1
  58. package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +1 -1
  59. package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +1 -1
  60. package/server/modules/orchestration/a2a/agent-card.ts +4 -4
  61. package/server/modules/orchestration/a2a/routes.ts +2 -6
  62. package/server/modules/orchestration/a2a/task-dispatcher.ts +345 -0
  63. package/server/modules/orchestration/index.ts +0 -2
  64. package/server/modules/orchestration/tasks/orchestration-task-store.ts +2 -2
  65. package/server/modules/orchestration/tasks/orchestration-task.routes.ts +17 -0
  66. package/server/modules/orchestration/tasks/orchestration-task.service.ts +19 -27
  67. package/server/modules/orchestration/tasks/orchestration-task.types.ts +2 -2
  68. package/server/modules/orchestration/tasks/task-run-graph.ts +1 -1
  69. package/server/modules/orchestration/workflows/workflow-runner.ts +52 -60
  70. package/server/modules/orchestration/workflows/workflow-trace.ts +2 -2
  71. package/server/modules/orchestration/workflows/workflow.types.ts +1 -1
  72. package/server/routes/git.js +89 -28
  73. package/server/services/public-api-manifest.js +0 -9
  74. package/dist/assets/index-CapxXSc7.js +0 -816
  75. package/dist/assets/index-DkT6-Pja.css +0 -32
  76. package/dist/hermes-agent.png +0 -0
  77. package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +0 -577
  78. package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +0 -1
  79. package/dist-server/server/services/hermes-gateway.js +0 -1467
  80. package/dist-server/server/services/hermes-gateway.js.map +0 -1
  81. package/dist-server/server/services/hermes-install-jobs.js +0 -676
  82. package/dist-server/server/services/hermes-install-jobs.js.map +0 -1
  83. package/scripts/hermes/configure-pixcode-mcp.mjs +0 -165
  84. package/scripts/hermes/pixcode-mcp-server.mjs +0 -1009
  85. package/scripts/smoke/hermes-api-install.mjs +0 -56
  86. package/scripts/smoke/hermes-gateway-persistence.mjs +0 -104
  87. package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +0 -426
  88. package/scripts/smoke/hermes-rest-chat-api.mjs +0 -162
  89. package/scripts/smoke/hermes-rest-chat-live.mjs +0 -45
  90. package/scripts/smoke/hermes-rest-codex-launch.mjs +0 -209
  91. package/scripts/smoke/hermes-rest-gateway.mjs +0 -79
  92. package/scripts/smoke/hermes-rest-live.mjs +0 -42
  93. package/scripts/smoke/hermes-roundtrip.mjs +0 -167
  94. package/scripts/smoke/hermes-settings-commands.mjs +0 -349
  95. package/scripts/smoke/hermes-smoke-launcher-guard.mjs +0 -34
  96. package/server/modules/orchestration/hermes/hermes.routes.ts +0 -642
  97. package/server/services/hermes-gateway.js +0 -1562
  98. package/server/services/hermes-install-jobs.js +0 -729
@@ -0,0 +1,345 @@
1
+ import crypto from 'node:crypto';
2
+
3
+ import { adapterRegistry } from '@/modules/orchestration/a2a/adapter-registry.js';
4
+ import { a2aBus } from '@/modules/orchestration/a2a/bus.js';
5
+ import { a2aTaskStore } from '@/modules/orchestration/a2a/task-store.js';
6
+ import type {
7
+ BusEvent,
8
+ Message,
9
+ SubmitTaskInput,
10
+ Task,
11
+ TaskState,
12
+ } from '@/modules/orchestration/a2a/types.js';
13
+ import type { TaskHandle } from '@/modules/orchestration/a2a/adapters/abstract-a2a.adapter.js';
14
+ import { portWatcher } from '@/modules/orchestration/preview/port-watcher.js';
15
+ import type { PreviewArtifactData } from '@/modules/orchestration/preview/types.js';
16
+ import {
17
+ type PermissionPolicy,
18
+ type PermissionPolicyContext,
19
+ } from '@/modules/orchestration/security/permission-policy.js';
20
+ import { workspaceManager } from '@/modules/orchestration/workspace/workspace-manager.js';
21
+ import type {
22
+ WorkspaceHandle,
23
+ WorkspaceKind,
24
+ WorkspaceMetadata,
25
+ } from '@/modules/orchestration/workspace/types.js';
26
+ import { WorkspaceError } from '@/modules/orchestration/workspace/types.js';
27
+
28
+ type RoutingHints = {
29
+ preferredAdapterId?: string;
30
+ preferredProvider?: string;
31
+ preferredSkillId?: string;
32
+ };
33
+
34
+ const TERMINAL: TaskState[] = ['completed', 'canceled', 'failed'];
35
+ const MAX_TASKS = 1000;
36
+ const activeWorkspaces = new Map<string, WorkspaceHandle>();
37
+ const activeHandles = new Map<string, TaskHandle>();
38
+ const previewStops = new Map<string, () => void>();
39
+ const taskUnsubs = new Map<string, () => void>();
40
+ const finalizingTasks = new Set<string>();
41
+
42
+ function newId(prefix: string): string {
43
+ return `${prefix}_${crypto.randomBytes(8).toString('hex')}`;
44
+ }
45
+
46
+ function isTerminalTaskState(state: TaskState): boolean {
47
+ return TERMINAL.includes(state);
48
+ }
49
+
50
+ function readRoutingHints(value: unknown): RoutingHints {
51
+ if (!value || typeof value !== 'object') {
52
+ return {};
53
+ }
54
+
55
+ const source = value as Record<string, unknown>;
56
+ return {
57
+ preferredAdapterId:
58
+ typeof source.preferredAdapterId === 'string' ? source.preferredAdapterId : undefined,
59
+ preferredProvider:
60
+ typeof source.preferredProvider === 'string' ? source.preferredProvider : undefined,
61
+ preferredSkillId:
62
+ typeof source.preferredSkillId === 'string' ? source.preferredSkillId : undefined,
63
+ };
64
+ }
65
+
66
+ function readString(value: unknown): string | undefined {
67
+ return typeof value === 'string' && value.trim() ? value : undefined;
68
+ }
69
+
70
+ function readBoolean(value: unknown): boolean | undefined {
71
+ return typeof value === 'boolean' ? value : undefined;
72
+ }
73
+
74
+ function readObject(value: unknown): Record<string, unknown> | undefined {
75
+ return value && typeof value === 'object' ? value as Record<string, unknown> : undefined;
76
+ }
77
+
78
+ function readWorkspaceKind(value: unknown): WorkspaceKind | undefined {
79
+ return value === 'host' || value === 'worktree' || value === 'docker' ? value : undefined;
80
+ }
81
+
82
+ function workspaceMetadata(workspace: WorkspaceHandle, keepAfterCompletion?: boolean): WorkspaceMetadata {
83
+ return {
84
+ id: workspace.id,
85
+ kind: workspace.kind,
86
+ path: workspace.path,
87
+ baseRef: workspace.baseRef,
88
+ branchName: workspace.branchName,
89
+ keepAfterCompletion,
90
+ };
91
+ }
92
+
93
+ function evictOldestTerminalTask(): boolean {
94
+ for (const [taskId, task] of a2aTaskStore.entries()) {
95
+ if (!isTerminalTaskState(task.state)) continue;
96
+ const unsub = taskUnsubs.get(taskId);
97
+ if (unsub) {
98
+ unsub();
99
+ taskUnsubs.delete(taskId);
100
+ }
101
+ a2aTaskStore.delete(taskId);
102
+ return true;
103
+ }
104
+ return false;
105
+ }
106
+
107
+ async function finalizeTerminalTask(task: Task): Promise<void> {
108
+ if (finalizingTasks.has(task.id)) return;
109
+ finalizingTasks.add(task.id);
110
+
111
+ const stopPreview = previewStops.get(task.id);
112
+ if (stopPreview) {
113
+ stopPreview();
114
+ previewStops.delete(task.id);
115
+ }
116
+
117
+ const workspace = activeWorkspaces.get(task.id);
118
+ try {
119
+ if (workspace) {
120
+ const diff = await workspace.diff();
121
+ if (diff.trim()) {
122
+ a2aBus.publish({
123
+ kind: 'artifact',
124
+ taskId: task.id,
125
+ artifact: {
126
+ artifactId: newId('art'),
127
+ type: 'file-diff',
128
+ parts: [{ kind: 'text', text: diff }],
129
+ metadata: {
130
+ source: 'workspace-diff',
131
+ workspaceId: workspace.id,
132
+ workspaceKind: workspace.kind,
133
+ baseRef: workspace.baseRef,
134
+ },
135
+ },
136
+ });
137
+ }
138
+
139
+ const keepAfterCompletion = task.metadata?.workspace &&
140
+ typeof task.metadata.workspace === 'object' &&
141
+ readBoolean((task.metadata.workspace as Record<string, unknown>).keepAfterCompletion);
142
+ if (workspace.kind !== 'host' && keepAfterCompletion !== true) {
143
+ await workspace.destroy();
144
+ }
145
+ }
146
+ } catch (error) {
147
+ const message = error instanceof Error ? error.message : String(error);
148
+ task.metadata = {
149
+ ...task.metadata,
150
+ workspaceFinalizationError: message,
151
+ };
152
+ task.updatedAt = Date.now();
153
+ a2aTaskStore.set(task);
154
+ } finally {
155
+ activeWorkspaces.delete(task.id);
156
+ activeHandles.delete(task.id);
157
+ const unsub = taskUnsubs.get(task.id);
158
+ if (unsub) {
159
+ unsub();
160
+ taskUnsubs.delete(task.id);
161
+ }
162
+ finalizingTasks.delete(task.id);
163
+ }
164
+ }
165
+
166
+ function attachBusToTask(task: Task): void {
167
+ if (taskUnsubs.has(task.id)) return;
168
+
169
+ const unsubscribe = a2aBus.subscribe(task.id, (event: BusEvent) => {
170
+ if (event.kind === 'task-state') {
171
+ task.state = event.state;
172
+ if (event.error) task.error = event.error;
173
+ task.updatedAt = Date.now();
174
+ a2aTaskStore.set(task);
175
+ if (isTerminalTaskState(event.state)) {
176
+ void finalizeTerminalTask(task);
177
+ }
178
+ } else if (event.kind === 'message') {
179
+ task.history.push(event.message);
180
+ task.updatedAt = Date.now();
181
+ a2aTaskStore.set(task);
182
+ } else if (event.kind === 'artifact') {
183
+ task.artifacts.push(event.artifact);
184
+ task.updatedAt = Date.now();
185
+ a2aTaskStore.set(task);
186
+ }
187
+ });
188
+ taskUnsubs.set(task.id, unsubscribe);
189
+ }
190
+
191
+ export function getA2ATask(taskId: string): Task | undefined {
192
+ return a2aTaskStore.get(taskId);
193
+ }
194
+
195
+ export function listA2AAgentCards() {
196
+ return adapterRegistry.agentCards();
197
+ }
198
+
199
+ export async function submitA2ATask(input: SubmitTaskInput): Promise<Task> {
200
+ const metadata = input.metadata as Record<string, unknown> | undefined;
201
+ const routing = readRoutingHints(metadata?.routing);
202
+ const adapter = adapterRegistry.resolve(input.adapterId, routing);
203
+ if (!adapter) {
204
+ throw new Error(`ADAPTER_NOT_FOUND: ${input.adapterId}`);
205
+ }
206
+
207
+ if (a2aTaskStore.size >= MAX_TASKS && !evictOldestTerminalTask()) {
208
+ throw new Error(`TASK_LIMIT: task store at capacity (${MAX_TASKS})`);
209
+ }
210
+
211
+ const userMessage: Message = input.message;
212
+ const task: Task = {
213
+ id: newId('task'),
214
+ contextId: input.contextId,
215
+ state: 'submitted',
216
+ history: [{ ...userMessage, taskId: '' }],
217
+ artifacts: [],
218
+ metadata: {
219
+ ...metadata,
220
+ adapterId: adapter.id,
221
+ adapterSelector: input.adapterId,
222
+ },
223
+ createdAt: Date.now(),
224
+ updatedAt: Date.now(),
225
+ };
226
+ task.history[0] = { ...userMessage, taskId: task.id };
227
+ a2aTaskStore.set(task);
228
+ attachBusToTask(task);
229
+
230
+ const workspaceOptions = (metadata?.workspace && typeof metadata.workspace === 'object'
231
+ ? metadata.workspace
232
+ : {}) as Record<string, unknown>;
233
+ let workspace: WorkspaceHandle;
234
+ try {
235
+ workspace = await workspaceManager.create({
236
+ taskId: task.id,
237
+ projectPath: readString(workspaceOptions.projectPath) ?? process.cwd(),
238
+ kind: readWorkspaceKind(workspaceOptions.kind) ?? readWorkspaceKind(metadata?.isolation),
239
+ baseRef: readString(workspaceOptions.baseRef) ?? readString(metadata?.baseRef) ?? 'HEAD',
240
+ keepAfterCompletion: readBoolean(workspaceOptions.keepAfterCompletion),
241
+ metadata: workspaceOptions,
242
+ });
243
+ } catch (err) {
244
+ const workspaceError = err instanceof WorkspaceError ? err : undefined;
245
+ a2aBus.publish({
246
+ kind: 'task-state',
247
+ taskId: task.id,
248
+ state: 'failed',
249
+ error: {
250
+ code: workspaceError?.code ?? 'WORKSPACE_CREATE_FAILED',
251
+ message: err instanceof Error ? err.message : String(err),
252
+ details: workspaceError?.details,
253
+ },
254
+ });
255
+ return a2aTaskStore.get(task.id) ?? task;
256
+ }
257
+
258
+ activeWorkspaces.set(task.id, workspace);
259
+ task.metadata = {
260
+ ...task.metadata,
261
+ workspace: workspaceMetadata(workspace, readBoolean(workspaceOptions.keepAfterCompletion)),
262
+ };
263
+ task.updatedAt = Date.now();
264
+ a2aTaskStore.set(task);
265
+
266
+ previewStops.set(
267
+ task.id,
268
+ portWatcher.watch({
269
+ taskId: task.id,
270
+ workspace,
271
+ onPort: (event) => {
272
+ const data: PreviewArtifactData = {
273
+ url: event.url,
274
+ proxiedUrl: `/preview/${event.port}/`,
275
+ port: event.port,
276
+ host: event.host,
277
+ processName: event.processName,
278
+ confidence: event.confidence,
279
+ };
280
+ a2aBus.publish({
281
+ kind: 'artifact',
282
+ taskId: task.id,
283
+ artifact: {
284
+ artifactId: newId('art'),
285
+ type: 'preview-url',
286
+ parts: [{ kind: 'data', data: { ...data } }],
287
+ metadata: {
288
+ source: 'port-watcher',
289
+ workspaceId: workspace.id,
290
+ },
291
+ },
292
+ });
293
+ },
294
+ }),
295
+ );
296
+
297
+ try {
298
+ const handle = await adapter.submitTask(task, {
299
+ cwd: workspace.path,
300
+ workspace,
301
+ model: readString(metadata?.model),
302
+ permissionMode: readString(metadata?.permissionMode),
303
+ permissionPolicy: readObject(metadata?.permissionPolicy) as PermissionPolicy | undefined,
304
+ permissionPolicyContext: readObject(metadata?.permissionPolicyContext) as PermissionPolicyContext | undefined,
305
+ toolsSettings: readObject(metadata?.toolsSettings),
306
+ });
307
+ activeHandles.set(task.id, handle);
308
+ handle.finished.finally(() => {
309
+ activeHandles.delete(task.id);
310
+ }).catch(() => undefined);
311
+ } catch (err) {
312
+ a2aBus.publish({
313
+ kind: 'task-state',
314
+ taskId: task.id,
315
+ state: 'failed',
316
+ error: {
317
+ code: 'ADAPTER_SUBMIT_FAILED',
318
+ message: err instanceof Error ? err.message : String(err),
319
+ },
320
+ });
321
+ }
322
+
323
+ return a2aTaskStore.get(task.id) ?? task;
324
+ }
325
+
326
+ export async function cancelA2ATask(taskId: string, adapterId?: string): Promise<Task | undefined> {
327
+ const task = a2aTaskStore.get(taskId);
328
+ if (!task) return undefined;
329
+
330
+ const handle = activeHandles.get(taskId);
331
+ if (handle) {
332
+ await handle.cancel();
333
+ return a2aTaskStore.get(taskId) ?? task;
334
+ }
335
+
336
+ const resolvedAdapterId = adapterId ?? readString(task.metadata?.adapterId);
337
+ const adapter = resolvedAdapterId ? adapterRegistry.resolve(resolvedAdapterId) : undefined;
338
+ if (adapter) {
339
+ await adapter.cancelTask(task.id);
340
+ return a2aTaskStore.get(task.id) ?? task;
341
+ }
342
+
343
+ a2aBus.publish({ kind: 'task-state', taskId: task.id, state: 'canceled' });
344
+ return a2aTaskStore.get(task.id) ?? task;
345
+ }
@@ -3,7 +3,6 @@
3
3
  // All cross-module consumers must import from here per
4
4
  // eslint.config.js boundaries rules.
5
5
 
6
- export { createHermesTaskRouter } from './a2a/routes.js';
7
6
  export { adapterRegistry } from './a2a/adapter-registry.js';
8
7
  export { ClaudeCodeA2AAdapter } from './a2a/adapters/claude-code.adapter.js';
9
8
  export { CodexA2AAdapter } from './a2a/adapters/codex.adapter.js';
@@ -31,7 +30,6 @@ export {
31
30
  } from './security/permission-policy.js';
32
31
  export { createOrchestrationTaskRouter } from './tasks/orchestration-task.routes.js';
33
32
  export { orchestrationTaskService } from './tasks/orchestration-task.service.js';
34
- export { createHermesRouter } from './hermes/hermes.routes.js';
35
33
  export { createWorkflowRouter } from './workflows/workflow.routes.js';
36
34
  export {
37
35
  listPendingApprovals,
@@ -20,8 +20,8 @@ export class OrchestrationTaskStore {
20
20
  return this.store.findWhere('orchestration_tasks', (r: OrchestrationTask) => r.id === id) ?? undefined;
21
21
  }
22
22
 
23
- getByHermesTaskId(hermesTaskId: string): OrchestrationTask | undefined {
24
- return this.store.findWhere('orchestration_tasks', (r: OrchestrationTask) => r.hermesTaskId === hermesTaskId) ?? undefined;
23
+ getByA2ATaskId(a2aTaskId: string): OrchestrationTask | undefined {
24
+ return this.store.findWhere('orchestration_tasks', (r: OrchestrationTask) => r.a2aTaskId === a2aTaskId) ?? undefined;
25
25
  }
26
26
 
27
27
  list(projectId?: string): OrchestrationTask[] {
@@ -1,5 +1,9 @@
1
1
  import express, { type Router } from 'express';
2
2
 
3
+ import {
4
+ getA2ATask,
5
+ listA2AAgentCards,
6
+ } from '@/modules/orchestration/a2a/task-dispatcher.js';
3
7
  import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
4
8
 
5
9
  export function createOrchestrationTaskRouter(): Router {
@@ -11,6 +15,19 @@ export function createOrchestrationTaskRouter(): Router {
11
15
  res.json({ tasks: orchestrationTaskService.list(projectId) });
12
16
  });
13
17
 
18
+ router.get('/agents', (_req, res) => {
19
+ res.json({ agents: listA2AAgentCards() });
20
+ });
21
+
22
+ router.get('/agent-tasks/:id', (req, res) => {
23
+ const task = getA2ATask(req.params.id);
24
+ if (!task) {
25
+ res.status(404).json({ error: { code: 'TASK_NOT_FOUND', message: req.params.id } });
26
+ return;
27
+ }
28
+ res.json(task);
29
+ });
30
+
14
31
  router.post('/tasks', (req, res) => {
15
32
  const projectId = typeof req.body?.projectId === 'string' ? req.body.projectId : 'default';
16
33
  const title = typeof req.body?.title === 'string' ? req.body.title.trim() : '';
@@ -1,6 +1,7 @@
1
1
  import { OrchestrationTaskStore } from '@/modules/orchestration/tasks/orchestration-task-store.js';
2
2
  import type { CreateOrchestrationTaskInput, DispatchOrchestrationTaskInput, OrchestrationTask } from '@/modules/orchestration/tasks/orchestration-task.types.js';
3
3
  import { a2aBus } from '@/modules/orchestration/a2a/bus.js';
4
+ import { submitA2ATask } from '@/modules/orchestration/a2a/task-dispatcher.js';
4
5
  import type { TaskState } from '@/modules/orchestration/a2a/types.js';
5
6
  import type { WorkflowNodeRun, WorkflowRun } from '@/modules/orchestration/workflows/workflow.types.js';
6
7
 
@@ -93,35 +94,26 @@ class OrchestrationTaskService {
93
94
  const task = this.store.get(taskId);
94
95
  if (!task) throw new Error('TASK_NOT_FOUND');
95
96
 
96
- const a2aResponse = await fetch(`http://127.0.0.1:${process.env.SERVER_PORT ?? '3001'}/hermes/tasks`, {
97
- method: 'POST',
98
- headers: { 'Content-Type': 'application/json' },
99
- body: JSON.stringify({
100
- adapterId: input.adapterId,
101
- message: {
102
- messageId: newId('msg'),
103
- role: 'user',
104
- parts: [{ kind: 'text', text: `${task.title}\n\n${task.description ?? ''}` }],
105
- },
106
- metadata: {
107
- isolation: input.isolation ?? 'worktree',
108
- model: input.model,
109
- permissionMode: input.permissionMode,
110
- workspace: {
111
- kind: input.isolation ?? 'worktree',
112
- projectPath: input.projectPath,
113
- },
114
- orchestrationTaskId: task.id,
97
+ const a2aTask = await submitA2ATask({
98
+ adapterId: input.adapterId,
99
+ message: {
100
+ messageId: newId('msg'),
101
+ role: 'user',
102
+ parts: [{ kind: 'text', text: `${task.title}\n\n${task.description ?? ''}` }],
103
+ },
104
+ metadata: {
105
+ isolation: input.isolation ?? 'worktree',
106
+ model: input.model,
107
+ permissionMode: input.permissionMode,
108
+ workspace: {
109
+ kind: input.isolation ?? 'worktree',
110
+ projectPath: input.projectPath,
115
111
  },
116
- }),
112
+ orchestrationTaskId: task.id,
113
+ },
117
114
  });
118
115
 
119
- const body = await a2aResponse.json().catch(() => null) as { id?: string; error?: { message?: string } } | null;
120
- if (!a2aResponse.ok || typeof body?.id !== 'string') {
121
- throw new Error(body?.error?.message ?? 'DISPATCH_FAILED');
122
- }
123
-
124
- task.hermesTaskId = body.id;
116
+ task.a2aTaskId = a2aTask.id;
125
117
  task.adapterId = input.adapterId;
126
118
  task.adapterSelector = input.adapterId;
127
119
  task.workspaceKind = input.isolation ?? 'worktree';
@@ -181,7 +173,7 @@ class OrchestrationTaskService {
181
173
  if (event.kind !== 'task-state') return;
182
174
  if (!TERMINAL_A2A_STATES.includes(event.state)) return;
183
175
 
184
- const orchTask = this.store.getByHermesTaskId(event.taskId);
176
+ const orchTask = this.store.getByA2ATaskId(event.taskId);
185
177
  if (!orchTask) return;
186
178
  if (orchTask.state === 'done' || orchTask.state === 'failed' || orchTask.state === 'canceled') return;
187
179
 
@@ -2,7 +2,7 @@ export type OrchestrationTaskState = 'todo' | 'in_progress' | 'in_review' | 'don
2
2
 
3
3
  export interface OrchestrationTask {
4
4
  id: string;
5
- hermesTaskId?: string;
5
+ a2aTaskId?: string;
6
6
  workflowRunIds?: string[];
7
7
  projectId: string;
8
8
  title: string;
@@ -12,7 +12,7 @@ export interface OrchestrationTask {
12
12
  id: string;
13
13
  label: string;
14
14
  status: 'pending' | 'passed' | 'failed';
15
- source: 'workflow' | 'hermes';
15
+ source: 'workflow' | 'agent';
16
16
  }>;
17
17
  changedFiles?: string[];
18
18
  adapterId?: string;
@@ -11,7 +11,7 @@ export interface TaskRunGraphCriterion {
11
11
  id: string;
12
12
  label: string;
13
13
  status: TaskRunGraphCriterionStatus;
14
- source: 'workflow' | 'hermes';
14
+ source: 'workflow' | 'agent';
15
15
  }
16
16
 
17
17
  export interface TaskRunGraphRunSummary {