@taico/worker 0.0.6 → 0.0.7

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.
@@ -17,6 +17,7 @@ export declare class Coordinator {
17
17
  private dependenciesAreDone;
18
18
  private getCurrentAgent;
19
19
  private getConcurrencyLimit;
20
+ private buildThreadContextInstructions;
20
21
  private buildPrompt;
21
22
  private executeTask;
22
23
  }
@@ -190,7 +190,21 @@ export class Coordinator {
190
190
  }
191
191
  return null;
192
192
  }
193
- buildPrompt(task, agent, mode, inputRequest) {
193
+ buildThreadContextInstructions(task, thread) {
194
+ return [
195
+ '',
196
+ 'Thread context:',
197
+ `- This task belongs to thread "${thread.id}" (${thread.title}).`,
198
+ `- This task is ${thread.parentTaskId === task.id ? 'the parent task' : 'an attached task'} in that thread.`,
199
+ `- Read shared thread memory at the start via mcp__context__get_thread_state_memory with threadId "${thread.id}".`,
200
+ `- Check sibling task status via mcp__tasks__list_tasks_by_thread with threadId "${thread.id}".`,
201
+ `- Keep decisions aligned with this shared memory and thread-level goal, not only this single task.`,
202
+ ];
203
+ }
204
+ buildPrompt(task, agent, mode, inputRequest, thread) {
205
+ const threadContextInstructions = thread
206
+ ? this.buildThreadContextInstructions(task, thread)
207
+ : [];
194
208
  if (mode === 'input_request' && inputRequest) {
195
209
  return [
196
210
  `You got triggered by an unanswered input request in task "${task.id}".`,
@@ -200,10 +214,18 @@ export class Coordinator {
200
214
  `Input request id: ${inputRequest.id}`,
201
215
  `Question: ${String(inputRequest.question ?? '')}`,
202
216
  '',
217
+ ...threadContextInstructions,
218
+ '',
203
219
  'Fetch the task, answer the input request assigned to you, and stop there.',
204
220
  ].join('\n');
205
221
  }
206
- return `You got triggered by new activity in task "${task.id}". Fetch the task and proceed according to the following instructions.\n\n\n ${agent.systemPrompt}`;
222
+ return [
223
+ `You got triggered by new activity in task "${task.id}".`,
224
+ 'Fetch the task and proceed according to the following instructions.',
225
+ ...threadContextInstructions,
226
+ '',
227
+ agent.systemPrompt,
228
+ ].join('\n');
207
229
  }
208
230
  async executeTask(task, agent, mode, inputRequest) {
209
231
  if (this.activeTaskIds.has(task.id)) {
@@ -244,6 +266,10 @@ export class Coordinator {
244
266
  // Prep workspace
245
267
  const workDir = await prepareWorkspace(task.id, agent.actorId, repoUrl);
246
268
  console.log(`- workspace prepped`);
269
+ const thread = await this.client.getThreadByTaskId(task.id);
270
+ if (thread) {
271
+ console.log(`- Task ${task.id} belongs to thread ${thread.id} (${thread.title})`);
272
+ }
247
273
  const run = await this.client.startRun(task.id);
248
274
  if (!run) {
249
275
  console.error(`Failed to create a run ❌`);
@@ -276,10 +302,11 @@ export class Coordinator {
276
302
  try {
277
303
  const results = await runner.run({
278
304
  taskId: task.id,
279
- prompt: this.buildPrompt(task, agent, mode, inputRequest),
305
+ prompt: this.buildPrompt(task, agent, mode, inputRequest, thread),
280
306
  cwd: workDir,
281
307
  runId: run.id,
282
308
  resume: sessionId ?? undefined,
309
+ agentSlug: agent.slug,
283
310
  }, {
284
311
  onEvent: (message) => {
285
312
  console.log(`[agent message] ⤵️`);
package/dist/Taico.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AgentResponseDto, type AgentRunResponseDto, type ProjectResponseDto, type TaskResponseDto } from "@taico/client";
1
+ import { type AgentResponseDto, type AgentRunResponseDto, type ProjectResponseDto, type TaskResponseDto, type ThreadResponseDto } from "@taico/client";
2
2
  export declare class Taico {
3
3
  constructor(baseUrl: string, accessToken: string);
4
4
  getAgent(agentSlug: string): Promise<AgentResponseDto | null>;
@@ -8,5 +8,6 @@ export declare class Taico {
8
8
  listTasks(page?: number, limit?: number): Promise<TaskResponseDto[]>;
9
9
  getTask(taskId: string): Promise<TaskResponseDto | null>;
10
10
  getProjectBySlug(slug: string): Promise<ProjectResponseDto | null>;
11
+ getThreadByTaskId(taskId: string): Promise<ThreadResponseDto | null>;
11
12
  startRun(taskId: string): Promise<AgentRunResponseDto | null>;
12
13
  }
package/dist/Taico.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Taico.ts - API client wrapper using generated services
2
- import { OpenAPI, ApiError, AgentService, TaskService, MetaProjectsService, AgentRunService, } from "@taico/client";
2
+ import { OpenAPI, ApiError, AgentService, TaskService, ThreadsService, MetaProjectsService, AgentRunService, } from "@taico/client";
3
3
  function isApiError(error) {
4
4
  return error instanceof ApiError;
5
5
  }
@@ -70,6 +70,17 @@ export class Taico {
70
70
  throw error;
71
71
  }
72
72
  }
73
+ async getThreadByTaskId(taskId) {
74
+ try {
75
+ return await ThreadsService.threadsControllerGetThreadByTaskId(taskId);
76
+ }
77
+ catch (error) {
78
+ if (isApiError(error) && error.status === 404) {
79
+ return null;
80
+ }
81
+ throw error;
82
+ }
83
+ }
73
84
  async startRun(taskId) {
74
85
  try {
75
86
  return await AgentRunService.agentRunsControllerCreateAgentRun({
@@ -1,4 +1,6 @@
1
1
  import { Event } from "@google/adk";
2
2
  export declare class ADKMessageFormatter {
3
+ private agentSlug?;
4
+ constructor(agentSlug?: string | undefined);
3
5
  format(message: Event): Array<string>;
4
6
  }
@@ -1,5 +1,10 @@
1
1
  export class ADKMessageFormatter {
2
+ agentSlug;
3
+ constructor(agentSlug) {
4
+ this.agentSlug = agentSlug;
5
+ }
2
6
  format(message) {
7
+ const agentLabel = this.agentSlug ? `@${this.agentSlug}` : 'Assistant';
3
8
  const content = message.content;
4
9
  if (!content) {
5
10
  return [];
@@ -11,19 +16,19 @@ export class ADKMessageFormatter {
11
16
  const partMessages = parts.map(part => {
12
17
  // Think
13
18
  if (part.thought) {
14
- return `💬 Thinking...`;
19
+ return `💬 ${agentLabel} Thinking...`;
15
20
  }
16
21
  // Tool call
17
22
  if (part.functionCall) {
18
- return `🔧 Tool call: ${part.functionCall.name}`;
23
+ return `🔧 ${agentLabel} Tool call: ${part.functionCall.name}`;
19
24
  }
20
25
  // Tool response
21
26
  if (part.functionResponse) {
22
- return `🔧 Tool response: ${part.functionResponse.name}`;
27
+ return `🔧 ${agentLabel} Tool response: ${part.functionResponse.name}`;
23
28
  }
24
29
  // Text
25
30
  if (part.text) {
26
- return `💬 Assistant: ${part.text}`;
31
+ return `💬 ${agentLabel}: ${part.text}`;
27
32
  }
28
33
  return null;
29
34
  }).filter(p => p != null);
@@ -1,5 +1,7 @@
1
1
  import { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
2
2
  export declare class ClaudeMessageFormatter {
3
+ private agentSlug?;
4
+ constructor(agentSlug?: string | undefined);
3
5
  format(message: SDKMessage): string | null;
4
6
  private formatAssistant;
5
7
  private formatUser;
@@ -1,4 +1,8 @@
1
1
  export class ClaudeMessageFormatter {
2
+ agentSlug;
3
+ constructor(agentSlug) {
4
+ this.agentSlug = agentSlug;
5
+ }
2
6
  format(message) {
3
7
  switch (message.type) {
4
8
  case 'assistant':
@@ -25,15 +29,16 @@ export class ClaudeMessageFormatter {
25
29
  const content = message.message?.content;
26
30
  if (!Array.isArray(content))
27
31
  return null;
32
+ const agentLabel = this.agentSlug ? `@${this.agentSlug}` : 'Assistant';
28
33
  for (const c of content) {
29
34
  if (c.type === 'tool_use') {
30
- parts.push(`🔧 Tool call: ${c.name}`);
35
+ parts.push(`🔧 ${agentLabel} Tool call: ${c.name}`);
31
36
  }
32
37
  else if (c.type === 'text') {
33
- parts.push(`💬 Assistant: ${c.text}`);
38
+ parts.push(`💬 ${agentLabel}: ${c.text}`);
34
39
  }
35
40
  else {
36
- parts.push(`💬 Assistant (${c.type})`);
41
+ parts.push(`💬 ${agentLabel} (${c.type})`);
37
42
  }
38
43
  }
39
44
  return parts.length ? parts.join('\n') : null;
@@ -58,20 +63,22 @@ export class ClaudeMessageFormatter {
58
63
  return parts.length ? parts.join('\n') : null;
59
64
  }
60
65
  formatResult(message) {
66
+ const agentLabel = this.agentSlug ? `@${this.agentSlug}` : 'Assistant';
61
67
  if (message.subtype === 'success' &&
62
68
  typeof message.result === 'string') {
63
69
  return [
64
- `--- Agent turn complete ---`,
70
+ `--- ${agentLabel} turn complete ---`,
65
71
  message.result,
66
72
  `---------------------------`,
67
73
  ].join('\n');
68
74
  }
69
- return `✅ Agent result received`;
75
+ return `✅ ${agentLabel} result received`;
70
76
  }
71
77
  formatSystem(message) {
78
+ const agentLabel = this.agentSlug ? `@${this.agentSlug}` : 'Assistant';
72
79
  if (message.subtype === 'init') {
73
80
  return [
74
- `🧠 Claude initialized`,
81
+ `🧠 ${agentLabel} Claude initialized`,
75
82
  `- Permissions: ${message.permissionMode}`,
76
83
  `- Tools: ${message.tools.length}`,
77
84
  `- MCP Servers: ${message.mcp_servers.length}`,
@@ -81,7 +88,7 @@ export class ClaudeMessageFormatter {
81
88
  `- Agents: ${message.agents?.length || 0}`,
82
89
  ].join('\n');
83
90
  }
84
- return `⚙️ System message`;
91
+ return `⚙️ ${agentLabel} System message`;
85
92
  }
86
93
  formatStreamEvent(_) {
87
94
  // usually too noisy — ignore by default
@@ -92,6 +99,7 @@ export class ClaudeMessageFormatter {
92
99
  return null;
93
100
  }
94
101
  formatAuthStatus(_) {
95
- return `🔐 Auth status updated`;
102
+ const agentLabel = this.agentSlug ? `@${this.agentSlug}` : 'Assistant';
103
+ return `🔐 ${agentLabel} Auth status updated`;
96
104
  }
97
105
  }
@@ -1,8 +1,12 @@
1
1
  import { AssistantMessage, Part, Event } from "@opencode-ai/sdk";
2
- export declare function opencodePartToText(part: Part): string;
2
+ export declare function opencodePartToText(part: Part, agentSlug?: string): string;
3
3
  export declare class OpencodeAsyncMessageFormatter {
4
+ private agentSlug?;
5
+ constructor(agentSlug?: string | undefined);
4
6
  format(event: Event): string | null;
5
7
  }
6
8
  export declare class OpencodeSyncMessageFormatter {
9
+ private agentSlug?;
10
+ constructor(agentSlug?: string | undefined);
7
11
  format(info: AssistantMessage, parts: Array<Part>): Array<string>;
8
12
  }
@@ -1,32 +1,37 @@
1
- export function opencodePartToText(part) {
1
+ export function opencodePartToText(part, agentSlug) {
2
+ const agentLabel = agentSlug ? `@${agentSlug}` : 'Assistant';
2
3
  switch (part.type) {
3
4
  case 'text':
4
- return `💬 Assistant: ${part.text}`;
5
+ return `💬 ${agentLabel}: ${part.text}`;
5
6
  case 'subtask':
6
- return `💬 Assistant: Creating subtask: ${part.description}`;
7
+ return `💬 ${agentLabel}: Creating subtask: ${part.description}`;
7
8
  case 'reasoning':
8
- return `💬 Assistant: Thinking...`;
9
+ return `💬 ${agentLabel}: Thinking...`;
9
10
  case 'file':
10
- return `📂 File ${part.filename || ''}`;
11
+ return `📂 ${agentLabel} File ${part.filename || ''}`;
11
12
  case 'tool':
12
- return `🔧 Tool call: ${part.tool}`;
13
+ return `🔧 ${agentLabel} Tool call: ${part.tool}`;
13
14
  case 'step-start':
14
- return `💬 Assistant: Starting step`;
15
+ return `💬 ${agentLabel}: Starting step`;
15
16
  case 'step-finish':
16
- return `💬 Assistant: Finish step: ${part.reason}`;
17
+ return `💬 ${agentLabel}: Finish step: ${part.reason}`;
17
18
  case 'snapshot':
18
- return `📸 Snapshot: ${part.snapshot}`;
19
+ return `📸 ${agentLabel} Snapshot: ${part.snapshot}`;
19
20
  case 'patch':
20
- return `🔧 Patching files: ${part.files}`;
21
+ return `🔧 ${agentLabel} Patching files: ${part.files}`;
21
22
  case 'agent':
22
- return `🤖 Agent: ${part.name}`;
23
+ return `🤖 ${agentLabel} Agent: ${part.name}`;
23
24
  case 'retry':
24
- return `🔄 Retrying error due to error '${part.error.name}'. Attempt ${part.attempt}...`;
25
+ return `🔄 ${agentLabel} Retrying error due to error '${part.error.name}'. Attempt ${part.attempt}...`;
25
26
  case 'compaction':
26
- return `🚜 Compacting`;
27
+ return `🚜 ${agentLabel} Compacting`;
27
28
  }
28
29
  }
29
30
  export class OpencodeAsyncMessageFormatter {
31
+ agentSlug;
32
+ constructor(agentSlug) {
33
+ this.agentSlug = agentSlug;
34
+ }
30
35
  // Only listen to part update (this is where tool calls and messages happen)
31
36
  format(event) {
32
37
  if (event.type !== 'message.part.updated') {
@@ -38,19 +43,24 @@ export class OpencodeAsyncMessageFormatter {
38
43
  }
39
44
  // Parse part into text
40
45
  const part = event.properties.part;
41
- const message = opencodePartToText(part);
46
+ const message = opencodePartToText(part, this.agentSlug);
42
47
  return message;
43
48
  }
44
49
  }
45
50
  export class OpencodeSyncMessageFormatter {
51
+ agentSlug;
52
+ constructor(agentSlug) {
53
+ this.agentSlug = agentSlug;
54
+ }
46
55
  format(info, parts) {
47
56
  const messages = [];
57
+ const agentLabel = this.agentSlug ? `@${this.agentSlug}` : 'Assistant';
48
58
  // Parse info
49
59
  if (info.error) {
50
- messages.push(`Error ${info.error.name}: ${JSON.stringify(info.error.data)}`);
60
+ messages.push(`❌ ${agentLabel} Error ${info.error.name}: ${JSON.stringify(info.error.data)}`);
51
61
  }
52
62
  // Parse parts
53
- const partMessages = parts.map(opencodePartToText);
63
+ const partMessages = parts.map(part => opencodePartToText(part, this.agentSlug));
54
64
  return [...messages, ...partMessages];
55
65
  }
56
66
  }
@@ -2,7 +2,6 @@ import { BaseAgentRunner } from "./BaseAgentRunner.js";
2
2
  import { AgentModelConfig, AgentRunContext } from "./AgentRunner.js";
3
3
  export declare class ADKAgentRunner extends BaseAgentRunner {
4
4
  readonly kind = "adk";
5
- private formatter;
6
5
  private modelId;
7
6
  private sessionService;
8
7
  constructor(modelConfig?: AgentModelConfig);
@@ -42,7 +42,6 @@ class NamespacedMCPToolset extends MCPToolset {
42
42
  }
43
43
  export class ADKAgentRunner extends BaseAgentRunner {
44
44
  kind = 'adk';
45
- formatter = new ADKMessageFormatter();
46
45
  modelId;
47
46
  sessionService = new InMemorySessionService();
48
47
  constructor(modelConfig = {}) {
@@ -50,6 +49,7 @@ export class ADKAgentRunner extends BaseAgentRunner {
50
49
  this.modelId = modelConfig.modelId ?? 'gemini-2.5-flash';
51
50
  }
52
51
  async runInternal(ctx, emit, setSession, onError) {
52
+ const formatter = new ADKMessageFormatter(ctx.agentSlug);
53
53
  let finalResult = '';
54
54
  // Init a session
55
55
  const session = await this.sessionService.createSession({
@@ -100,7 +100,7 @@ export class ADKAgentRunner extends BaseAgentRunner {
100
100
  });
101
101
  for await (const msg of stream) {
102
102
  // map → string
103
- const messages = this.formatter.format(msg);
103
+ const messages = formatter.format(msg);
104
104
  messages.forEach(async (message) => {
105
105
  await emit(message);
106
106
  });
@@ -20,6 +20,8 @@ export type AgentRunContext = {
20
20
  /** For backend traceability */
21
21
  runId: string;
22
22
  model?: Model;
23
+ /** Agent slug for personalized activity messages */
24
+ agentSlug?: string;
23
25
  };
24
26
  export type AgentRunCallbacks = {
25
27
  /** Called once when a session/run ID is known */
@@ -2,7 +2,6 @@ import { BaseAgentRunner } from "./BaseAgentRunner.js";
2
2
  import { AgentModelConfig, AgentRunContext } from "./AgentRunner.js";
3
3
  export declare class ClaudeAgentRunner extends BaseAgentRunner {
4
4
  readonly kind = "claude";
5
- private formatter;
6
5
  constructor(_modelConfig?: AgentModelConfig);
7
6
  protected runInternal(ctx: AgentRunContext, emit: (msg: string) => Promise<void>, setSession: (id: string) => Promise<void>, onError?: (error: {
8
7
  message: string;
@@ -5,11 +5,11 @@ import { ClaudeMessageFormatter } from "../formatters/ClaudeMessageFormatter.js"
5
5
  import { ACCESS_TOKEN, BASE_URL, RUN_ID_HEADER } from "../helpers/config.js";
6
6
  export class ClaudeAgentRunner extends BaseAgentRunner {
7
7
  kind = 'claude';
8
- formatter = new ClaudeMessageFormatter();
9
8
  constructor(_modelConfig = {}) {
10
9
  super();
11
10
  }
12
11
  async runInternal(ctx, emit, setSession, onError) {
12
+ const formatter = new ClaudeMessageFormatter(ctx.agentSlug);
13
13
  let finalResult = '';
14
14
  const stream = query({
15
15
  prompt: ctx.prompt,
@@ -56,7 +56,7 @@ export class ClaudeAgentRunner extends BaseAgentRunner {
56
56
  await setSession(msg.session_id);
57
57
  }
58
58
  // map → string
59
- const text = this.formatter.format(msg);
59
+ const text = formatter.format(msg);
60
60
  if (text)
61
61
  await emit(text);
62
62
  if (msg.type === 'result' && msg.subtype === 'success') {
@@ -10,6 +10,7 @@ export class GitHubCopilotAgentRunner extends BaseAgentRunner {
10
10
  this.model = modelConfig.modelId ?? 'gpt-5.2-codex';
11
11
  }
12
12
  async runInternal(ctx, emit, setSession, onError) {
13
+ const agentLabel = ctx.agentSlug ? `@${ctx.agentSlug}` : 'Assistant';
13
14
  return new Promise(async (resolve, reject) => {
14
15
  try {
15
16
  // Init client
@@ -57,17 +58,17 @@ export class GitHubCopilotAgentRunner extends BaseAgentRunner {
57
58
  resolve(lastAssistantMessage);
58
59
  });
59
60
  session.on('assistant.reasoning', (reasoning) => {
60
- void emit(`💬 Thinking... ${reasoning.data.content}`);
61
+ void emit(`💬 ${agentLabel} Thinking... ${reasoning.data.content}`);
61
62
  });
62
63
  session.on('assistant.message', (message) => {
63
64
  lastAssistantMessage = message.data.content ?? '';
64
- void emit(`💬 Assistant: ${message.data.content}`);
65
+ void emit(`💬 ${agentLabel}: ${message.data.content}`);
65
66
  });
66
67
  session.on('tool.execution_start', (toolCall) => {
67
- void emit(`🔧 Tool call: ${toolCall.data.toolName}`);
68
+ void emit(`🔧 ${agentLabel} Tool call: ${toolCall.data.toolName}`);
68
69
  });
69
70
  session.on('tool.execution_complete', () => {
70
- void emit(`🔧 Tool response`);
71
+ void emit(`🔧 ${agentLabel} Tool response`);
71
72
  });
72
73
  // Fire up the prompt
73
74
  await session.send({
@@ -3,7 +3,6 @@ import { AgentModelConfig, AgentRunContext } from "./AgentRunner.js";
3
3
  export declare class OpencodeAgentRunner extends BaseAgentRunner {
4
4
  readonly kind = "opencode";
5
5
  private static chdirLock;
6
- private formatter;
7
6
  private client;
8
7
  private abortController;
9
8
  private close;
@@ -8,7 +8,6 @@ export class OpencodeAgentRunner extends BaseAgentRunner {
8
8
  // Mutex for process.chdir: serializes all instances so only one
9
9
  // changes the working directory at a time.
10
10
  static chdirLock = Promise.resolve();
11
- formatter = new OpencodeAsyncMessageFormatter();
12
11
  client = null;
13
12
  abortController = new AbortController();
14
13
  close = () => { };
@@ -105,6 +104,7 @@ export class OpencodeAgentRunner extends BaseAgentRunner {
105
104
  throw new Error(`Failed to start Opencode on ports ${PORT_START}–${PORT_END}: ${String(lastError)}`);
106
105
  }
107
106
  async runInternal(ctx, emit, setSession, onError) {
107
+ const formatter = new OpencodeAsyncMessageFormatter(ctx.agentSlug);
108
108
  // Start client
109
109
  await this.initBullshit({ runId: ctx.runId, cwd: ctx.cwd });
110
110
  // await this.init({ runId: ctx.runId });
@@ -157,7 +157,7 @@ export class OpencodeAgentRunner extends BaseAgentRunner {
157
157
  console.log('session.idle');
158
158
  break;
159
159
  }
160
- const message = this.formatter.format(event);
160
+ const message = formatter.format(event);
161
161
  if (message) {
162
162
  emit(message);
163
163
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taico/worker",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "MIT",