@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.
- package/dist/index.js +19 -63
- package/package.json +1 -1
- 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.
|
|
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, {
|
|
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
|
|
6250
|
+
const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `
|
|
6271
6251
|
|
|
6272
|
-
${task.description
|
|
6252
|
+
${task.description}` : ""}`;
|
|
6273
6253
|
let userPart;
|
|
6274
6254
|
if (task.followup) {
|
|
6275
6255
|
const cleanFollowup = stripSelfMention(task.followup, preferType);
|
|
6276
|
-
userPart =
|
|
6277
|
-
|
|
6278
|
-
${cleanFollowup}
|
|
6256
|
+
userPart = `${issueContext}
|
|
6279
6257
|
|
|
6280
6258
|
---
|
|
6281
|
-
|
|
6259
|
+
|
|
6260
|
+
## New user message \u2014 current request
|
|
6261
|
+
|
|
6262
|
+
${cleanFollowup}`;
|
|
6282
6263
|
} else {
|
|
6283
|
-
userPart = stripSelfMention(
|
|
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
|
|
6373
|
+
const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `
|
|
6393
6374
|
|
|
6394
|
-
${task.description
|
|
6375
|
+
${task.description}` : ""}`;
|
|
6395
6376
|
let userPart;
|
|
6396
6377
|
if (task.followup) {
|
|
6397
6378
|
const cleanFollowup = stripSelfMention(task.followup, preferType);
|
|
6398
|
-
userPart =
|
|
6399
|
-
|
|
6400
|
-
${cleanFollowup}
|
|
6379
|
+
userPart = `${issueContext}
|
|
6401
6380
|
|
|
6402
6381
|
---
|
|
6403
|
-
|
|
6382
|
+
|
|
6383
|
+
## New user message \u2014 current request
|
|
6384
|
+
|
|
6385
|
+
${cleanFollowup}`;
|
|
6404
6386
|
} else {
|
|
6405
|
-
userPart = stripSelfMention(
|
|
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
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.
|
|
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, {
|
|
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
|
-
|
|
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
|
|
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 =
|
|
634
|
+
userPart = `${issueContext}\n\n---\n\n## New user message — current request\n\n${cleanFollowup}`;
|
|
659
635
|
} else {
|
|
660
|
-
userPart = stripSelfMention(
|
|
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
|
|
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 =
|
|
714
|
+
userPart = `${issueContext}\n\n---\n\n## New user message — current request\n\n${cleanFollowup}`;
|
|
739
715
|
} else {
|
|
740
|
-
userPart = stripSelfMention(
|
|
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
|
|
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)}`);
|