@shipers-dev/multi 0.7.0 → 0.8.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 (3) hide show
  1. package/dist/index.js +19 -63
  2. package/package.json +1 -1
  3. package/src/index.ts +11 -52
package/dist/index.js CHANGED
@@ -5599,7 +5599,7 @@ var LOG_PATH2 = join2(MULTI_DIR, "logs", "agent.log");
5599
5599
  var SKILLS_DIR = join2(MULTI_DIR, "skills");
5600
5600
  var STOP_PATH = join2(MULTI_DIR, "stop.flag");
5601
5601
  var TASKS_DB_PATH = join2(MULTI_DIR, "tasks.db");
5602
- var VERSION = "0.7.0";
5602
+ var VERSION = "0.8.0";
5603
5603
  var COMMANDS = {
5604
5604
  setup: "Register this device with a workspace",
5605
5605
  connect: "Connect device to realtime hub and execute assigned tasks",
@@ -5838,26 +5838,6 @@ async function cmdConnect(apiUrl, config) {
5838
5838
  workerWake = null;
5839
5839
  } catch {}
5840
5840
  };
5841
- const localAgentIds = new Set;
5842
- const refreshLocalAgents = async () => {
5843
- try {
5844
- const res = await apiClient.get(`${apiUrl}/api/devices/${config.deviceId}/agents`);
5845
- const rows = res.data?.results || res.data || [];
5846
- if (Array.isArray(rows)) {
5847
- localAgentIds.clear();
5848
- for (const a of rows)
5849
- if (a?.id)
5850
- localAgentIds.add(a.id);
5851
- }
5852
- } catch {}
5853
- };
5854
- await refreshLocalAgents();
5855
- const enqueueLocal = (task) => {
5856
- const taskId = task?.issue_id ? `${task.issue_id}-${Date.now()}` : crypto.randomUUID();
5857
- db.run("INSERT INTO tasks (id, status, payload) VALUES (?, ?, ?)", [taskId, "pending", JSON.stringify(task)]);
5858
- notifyWorker();
5859
- return taskId;
5860
- };
5861
5841
  const port = await pickFreePort();
5862
5842
  const expectedAuth = `Bearer ${config.dispatchSecret}`;
5863
5843
  const server = Bun.serve({
@@ -5942,7 +5922,7 @@ async function cmdConnect(apiUrl, config) {
5942
5922
  db.run("UPDATE tasks SET status = 'running', started_at = unixepoch(), attempts = attempts + 1 WHERE id = ?", [row.id]);
5943
5923
  try {
5944
5924
  const task = JSON.parse(row.payload);
5945
- await handleRunTask(apiUrl, config.deviceId, task, detected, { localAgentIds, enqueueLocal, refreshLocalAgents });
5925
+ await handleRunTask(apiUrl, config.deviceId, task, detected, {});
5946
5926
  db.run("UPDATE tasks SET status = 'done', finished_at = unixepoch() WHERE id = ?", [row.id]);
5947
5927
  } catch (e) {
5948
5928
  log(`task ${row.id} error: ${String(e)}`);
@@ -6267,20 +6247,21 @@ _${bits.join(" \xB7 ")}_`);
6267
6247
  const useAcpx = !useAcp && preferType && ["pi", "codex", "openclaw"].includes(preferType) && process.env.MULTI_LEGACY !== "1";
6268
6248
  try {
6269
6249
  if (useAcp) {
6270
- const base = `${task.title}
6250
+ const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `
6271
6251
 
6272
- ${task.description || ""}`.trim();
6252
+ ${task.description}` : ""}`;
6273
6253
  let userPart;
6274
6254
  if (task.followup) {
6275
6255
  const cleanFollowup = stripSelfMention(task.followup, preferType);
6276
- userPart = `# New user message \u2014 THIS is the current request
6277
-
6278
- ${cleanFollowup}
6256
+ userPart = `${issueContext}
6279
6257
 
6280
6258
  ---
6281
- *Earlier thread (${task.key} "${task.title}") is only background context. Do not re-address the original task unless the new message explicitly refers back to it.*`;
6259
+
6260
+ ## New user message \u2014 current request
6261
+
6262
+ ${cleanFollowup}`;
6282
6263
  } else {
6283
- userPart = stripSelfMention(base || task.title, preferType);
6264
+ userPart = stripSelfMention(issueContext, preferType);
6284
6265
  }
6285
6266
  if (attachmentRefs.length) {
6286
6267
  const lines = attachmentRefs.map((a) => `- ${a.filename}: ${a.path}`).join(`
@@ -6389,20 +6370,21 @@ ${body}
6389
6370
  }
6390
6371
  } catch {}
6391
6372
  preamble += await buildPlanningPreamble(apiUrl, task);
6392
- const base = `${task.title}
6373
+ const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `
6393
6374
 
6394
- ${task.description || ""}`.trim();
6375
+ ${task.description}` : ""}`;
6395
6376
  let userPart;
6396
6377
  if (task.followup) {
6397
6378
  const cleanFollowup = stripSelfMention(task.followup, preferType);
6398
- userPart = `# New user message \u2014 THIS is the current request
6399
-
6400
- ${cleanFollowup}
6379
+ userPart = `${issueContext}
6401
6380
 
6402
6381
  ---
6403
- *Earlier thread (${task.key} "${task.title}") is only background context. Do not re-address the original task unless the new message explicitly refers back to it.*`;
6382
+
6383
+ ## New user message \u2014 current request
6384
+
6385
+ ${cleanFollowup}`;
6404
6386
  } else {
6405
- userPart = stripSelfMention(base || task.title, preferType);
6387
+ userPart = stripSelfMention(issueContext, preferType);
6406
6388
  }
6407
6389
  if (attachmentRefs.length) {
6408
6390
  const lines = attachmentRefs.map((a) => `- ${a.filename}: ${a.path}`).join(`
@@ -6593,20 +6575,7 @@ async function executePlanActions(apiUrl, parentTask, actions, ctx) {
6593
6575
  continue;
6594
6576
  }
6595
6577
  const created = res.data;
6596
- lines.push(`- \u2713 created **${created.key}** \u2014 ${created.title}${created.assignee_id ? ` \u2192 @${created.assignee_id}` : ""}`);
6597
- if (created.assignee_type === "agent" && created.assignee_id && ctx.localAgentIds.has(created.assignee_id)) {
6598
- ctx.enqueueLocal({
6599
- issue_id: created.id,
6600
- key: created.key,
6601
- title: created.title,
6602
- description: created.description,
6603
- agent_id: created.assignee_id,
6604
- session_id: null,
6605
- working_dir: parentTask.working_dir || null,
6606
- planning_depth: depth + 1
6607
- });
6608
- lines.push(` \u21B3 dispatched locally (same runtime)`);
6609
- }
6578
+ lines.push(`- \u2713 created **${created.key}** \u2014 ${created.title}${created.assignee_id ? ` \u2192 @${created.assignee_id}` : ""} (autonomy=${created.autonomy_level || "ask"})`);
6610
6579
  } else if (a.type === "update") {
6611
6580
  const res = await apiClient.post(`${apiUrl}/api/issues/agent/mutate`, { action: "update", ...a }, { headers });
6612
6581
  if (!res.success) {
@@ -6621,19 +6590,6 @@ async function executePlanActions(apiUrl, parentTask, actions, ctx) {
6621
6590
  continue;
6622
6591
  }
6623
6592
  lines.push(`- \u2713 delegated ${res.data.key} \u2192 ${a.assignee_id}`);
6624
- if (ctx.localAgentIds.has(a.assignee_id)) {
6625
- ctx.enqueueLocal({
6626
- issue_id: res.data.id,
6627
- key: res.data.key,
6628
- title: res.data.title,
6629
- description: res.data.description,
6630
- agent_id: a.assignee_id,
6631
- session_id: res.data.session_id || null,
6632
- working_dir: parentTask.working_dir || null,
6633
- planning_depth: depth + 1
6634
- });
6635
- lines.push(` \u21B3 dispatched locally (same runtime)`);
6636
- }
6637
6593
  }
6638
6594
  } catch (e) {
6639
6595
  lines.push(`- \u274C ${a.type} failed: ${String(e)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipers-dev/multi",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "multi-agent": "./dist/index.js"
package/src/index.ts CHANGED
@@ -17,7 +17,7 @@ const LOG_PATH = join(MULTI_DIR, 'logs', 'agent.log');
17
17
  const SKILLS_DIR = join(MULTI_DIR, 'skills');
18
18
  const STOP_PATH = join(MULTI_DIR, 'stop.flag');
19
19
  const TASKS_DB_PATH = join(MULTI_DIR, 'tasks.db');
20
- const VERSION = '0.7.0';
20
+ const VERSION = '0.8.0';
21
21
 
22
22
  const COMMANDS = {
23
23
  setup: 'Register this device with a workspace',
@@ -257,28 +257,6 @@ async function cmdConnect(apiUrl: string, config: Config) {
257
257
  let workerWake: (() => void) | null = null;
258
258
  const notifyWorker = () => { try { workerWake?.(); workerWake = null; } catch {} };
259
259
 
260
- // Agents linked to this device (refreshed periodically). Used to short-circuit
261
- // server dispatch when an agent-created child issue is assigned to an agent
262
- // running on the same runtime.
263
- const localAgentIds = new Set<string>();
264
- const refreshLocalAgents = async () => {
265
- try {
266
- const res = await apiClient.get<any>(`${apiUrl}/api/devices/${config.deviceId}/agents`);
267
- const rows = res.data?.results || res.data || [];
268
- if (Array.isArray(rows)) {
269
- localAgentIds.clear();
270
- for (const a of rows) if (a?.id) localAgentIds.add(a.id);
271
- }
272
- } catch {}
273
- };
274
- await refreshLocalAgents();
275
-
276
- const enqueueLocal = (task: any) => {
277
- const taskId = task?.issue_id ? `${task.issue_id}-${Date.now()}` : crypto.randomUUID();
278
- db.run('INSERT INTO tasks (id, status, payload) VALUES (?, ?, ?)', [taskId, 'pending', JSON.stringify(task)]);
279
- notifyWorker();
280
- return taskId;
281
- };
282
260
 
283
261
  // Local HTTP server on a free port
284
262
  const port = await pickFreePort();
@@ -351,7 +329,7 @@ async function cmdConnect(apiUrl: string, config: Config) {
351
329
  db.run("UPDATE tasks SET status = 'running', started_at = unixepoch(), attempts = attempts + 1 WHERE id = ?", [row.id]);
352
330
  try {
353
331
  const task = JSON.parse(row.payload);
354
- await handleRunTask(apiUrl, config.deviceId!, task, detected, { localAgentIds, enqueueLocal, refreshLocalAgents });
332
+ await handleRunTask(apiUrl, config.deviceId!, task, detected, {});
355
333
  db.run("UPDATE tasks SET status = 'done', finished_at = unixepoch() WHERE id = ?", [row.id]);
356
334
  } catch (e) {
357
335
  log(`task ${row.id} error: ${String(e)}`);
@@ -430,9 +408,7 @@ async function parseTunnelUrl(stream: ReadableStream<Uint8Array>): Promise<strin
430
408
  }
431
409
 
432
410
  interface RuntimeCtx {
433
- localAgentIds: Set<string>;
434
- enqueueLocal: (task: any) => string;
435
- refreshLocalAgents: () => Promise<void>;
411
+ // Reserved for future runtime-scoped helpers.
436
412
  }
437
413
 
438
414
  async function handleRunTask(apiUrl: string, deviceId: string, task: any, detected: { type: string; path: string }[], ctx?: RuntimeCtx) {
@@ -651,13 +627,13 @@ async function handleRunTask(apiUrl: string, deviceId: string, task: any, detect
651
627
 
652
628
  try {
653
629
  if (useAcp) {
654
- const base = `${task.title}\n\n${task.description || ''}`.trim();
630
+ const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `\n\n${task.description}` : ''}`;
655
631
  let userPart: string;
656
632
  if (task.followup) {
657
633
  const cleanFollowup = stripSelfMention(task.followup, preferType);
658
- userPart = `# New user message — THIS is the current request\n\n${cleanFollowup}\n\n---\n*Earlier thread (${task.key} "${task.title}") is only background context. Do not re-address the original task unless the new message explicitly refers back to it.*`;
634
+ userPart = `${issueContext}\n\n---\n\n## New user message — current request\n\n${cleanFollowup}`;
659
635
  } else {
660
- userPart = stripSelfMention(base || task.title, preferType);
636
+ userPart = stripSelfMention(issueContext, preferType);
661
637
  }
662
638
  if (attachmentRefs.length) {
663
639
  const lines = attachmentRefs.map(a => `- ${a.filename}: ${a.path}`).join('\n');
@@ -731,13 +707,13 @@ async function handleRunTask(apiUrl: string, deviceId: string, task: any, detect
731
707
  }
732
708
  } catch {}
733
709
  preamble += await buildPlanningPreamble(apiUrl, task);
734
- const base = `${task.title}\n\n${task.description || ''}`.trim();
710
+ const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `\n\n${task.description}` : ''}`;
735
711
  let userPart: string;
736
712
  if (task.followup) {
737
713
  const cleanFollowup = stripSelfMention(task.followup, preferType);
738
- userPart = `# New user message — THIS is the current request\n\n${cleanFollowup}\n\n---\n*Earlier thread (${task.key} "${task.title}") is only background context. Do not re-address the original task unless the new message explicitly refers back to it.*`;
714
+ userPart = `${issueContext}\n\n---\n\n## New user message — current request\n\n${cleanFollowup}`;
739
715
  } else {
740
- userPart = stripSelfMention(base || task.title, preferType);
716
+ userPart = stripSelfMention(issueContext, preferType);
741
717
  }
742
718
  if (attachmentRefs.length) {
743
719
  const lines = attachmentRefs.map(a => `- ${a.filename}: ${a.path}`).join('\n');
@@ -884,7 +860,7 @@ async function executePlanActions(apiUrl: string, parentTask: any, actions: Plan
884
860
  actions = actions.slice(0, PLAN_ACTION_LIMIT);
885
861
  }
886
862
  // Prevent agent recursion: a child turn's plan cannot itself spawn more children.
887
- // `planning_depth` is propagated from parent task; depth >= 1 blocks create/delegate.
863
+ // `planning_depth` is carried on each dispatched task (set server-side from issue row).
888
864
  const depth = typeof parentTask.planning_depth === 'number' ? parentTask.planning_depth : 0;
889
865
  if (depth >= 1) {
890
866
  const blocked = actions.filter(a => a.type === 'create' || a.type === 'delegate').length;
@@ -916,16 +892,7 @@ async function executePlanActions(apiUrl: string, parentTask: any, actions: Plan
916
892
  const res = await apiClient.post<any>(`${apiUrl}/api/issues/agent/mutate`, body, { headers });
917
893
  if (!res.success) { lines.push(`- ❌ create "${a.title}": ${res.error || res.status}`); continue; }
918
894
  const created = res.data;
919
- lines.push(`- ✓ created **${created.key}** — ${created.title}${created.assignee_id ? ` → @${created.assignee_id}` : ''}`);
920
- // Same-runtime shortcut: if new issue's agent is linked locally, enqueue directly.
921
- if (created.assignee_type === 'agent' && created.assignee_id && ctx.localAgentIds.has(created.assignee_id)) {
922
- ctx.enqueueLocal({
923
- issue_id: created.id, key: created.key, title: created.title, description: created.description,
924
- agent_id: created.assignee_id, session_id: null, working_dir: parentTask.working_dir || null,
925
- planning_depth: depth + 1,
926
- });
927
- lines.push(` ↳ dispatched locally (same runtime)`);
928
- }
895
+ lines.push(`- ✓ created **${created.key}** — ${created.title}${created.assignee_id ? ` → @${created.assignee_id}` : ''} (autonomy=${created.autonomy_level || 'ask'})`);
929
896
  } else if (a.type === 'update') {
930
897
  const res = await apiClient.post<any>(`${apiUrl}/api/issues/agent/mutate`, { action: 'update', ...a }, { headers });
931
898
  if (!res.success) { lines.push(`- ❌ update ${a.id}: ${res.error || res.status}`); continue; }
@@ -934,14 +901,6 @@ async function executePlanActions(apiUrl: string, parentTask: any, actions: Plan
934
901
  const res = await apiClient.post<any>(`${apiUrl}/api/issues/agent/mutate`, { action: 'update', id: a.id, assignee_type: 'agent', assignee_id: a.assignee_id, status: 'todo' }, { headers });
935
902
  if (!res.success) { lines.push(`- ❌ delegate ${a.id}: ${res.error || res.status}`); continue; }
936
903
  lines.push(`- ✓ delegated ${res.data.key} → ${a.assignee_id}`);
937
- if (ctx.localAgentIds.has(a.assignee_id)) {
938
- ctx.enqueueLocal({
939
- issue_id: res.data.id, key: res.data.key, title: res.data.title, description: res.data.description,
940
- agent_id: a.assignee_id, session_id: res.data.session_id || null, working_dir: parentTask.working_dir || null,
941
- planning_depth: depth + 1,
942
- });
943
- lines.push(` ↳ dispatched locally (same runtime)`);
944
- }
945
904
  }
946
905
  } catch (e) {
947
906
  lines.push(`- ❌ ${a.type} failed: ${String(e)}`);