@shipers-dev/multi 0.7.0 → 0.8.0
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 +357 -401
- package/package.json +1 -1
- package/src/index.ts +12 -53
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');
|
|
@@ -876,7 +852,7 @@ function extractPlanActions(text: string): PlanAction[] {
|
|
|
876
852
|
|
|
877
853
|
const PLAN_ACTION_LIMIT = 10;
|
|
878
854
|
|
|
879
|
-
async function executePlanActions(apiUrl: string, parentTask: any, actions: PlanAction[],
|
|
855
|
+
async function executePlanActions(apiUrl: string, parentTask: any, actions: PlanAction[], _ctx: RuntimeCtx): Promise<string> {
|
|
880
856
|
const lines: string[] = [];
|
|
881
857
|
let truncated = false;
|
|
882
858
|
if (actions.length > PLAN_ACTION_LIMIT) {
|
|
@@ -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)}`);
|