@shipers-dev/multi 0.64.0 → 0.65.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.
Files changed (2) hide show
  1. package/dist/index.js +196 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -35192,6 +35192,10 @@ _${bits.join(" · ")}_`);
35192
35192
  const acpCapable = detected.filter((d) => d.type === "claude-code");
35193
35193
  const useAcp = preferType !== "pi" && acpCapable.length > 0 && process.env.MULTI_LEGACY !== "1";
35194
35194
  const useAcpx = !useAcp && preferType && ["pi", "codex", "openclaw"].includes(preferType) && process.env.MULTI_LEGACY !== "1";
35195
+ let issueHistoryBlock = "";
35196
+ if (tenantWsId && ISSUE_BASE) {
35197
+ issueHistoryBlock = await fetchIssueHistory(apiUrl, tenantWsId, projectId, issueId, log3);
35198
+ }
35195
35199
  try {
35196
35200
  if (useAcp) {
35197
35201
  const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `
@@ -35209,6 +35213,20 @@ ${task.description}` : ""}`;
35209
35213
  ${cleanFollowup}`;
35210
35214
  } else {
35211
35215
  userPart = stripSelfMention(issueContext, preferType);
35216
+ userPart += `
35217
+
35218
+ ---
35219
+
35220
+ You have been assigned this issue. Investigate and resolve it. Read the codebase, understand the problem, implement a fix or complete the described work, and use your tools to make progress. When finished, emit a multi-plan action to mark the issue done (or blocked if you need human input).`;
35221
+ }
35222
+ if (issueHistoryBlock) {
35223
+ userPart += `
35224
+
35225
+ ---
35226
+
35227
+ ## Issue history (prior activity)
35228
+
35229
+ ${issueHistoryBlock}`;
35212
35230
  }
35213
35231
  if (attachmentRefs.length) {
35214
35232
  const lines = attachmentRefs.map((a) => `- ${a.filename}: ${a.path}`).join(`
@@ -35227,6 +35245,7 @@ Note: if (and only if) you produce binary or large artifact outputs (screenshots
35227
35245
  Respond in the chat. Only if you produce large artifact files (screenshots, data exports, generated source code), write them under ${outDir}. Do not put your answer in a file.`;
35228
35246
  }
35229
35247
  let preamble = "";
35248
+ let preambleFailed = false;
35230
35249
  try {
35231
35250
  if (tenantWsId) {
35232
35251
  const agentRes = await apiClient.get(`${apiUrl}/api/workspaces/${tenantWsId}/agents/${task.agent_id}`);
@@ -35256,7 +35275,9 @@ ${body}
35256
35275
  }
35257
35276
  }
35258
35277
  } catch (e) {
35278
+ preambleFailed = true;
35259
35279
  log3(`preamble fetch failed: ${String(e)}`);
35280
+ await postBoundChatMessage(apiUrl, task, `⚠️ System: agent preamble (prompt + skills) fetch failed — the agent is running without its role definition. Error: ${String(e).slice(0, 200)}`);
35260
35281
  }
35261
35282
  preamble += await buildPlanningPreamble(apiUrl, task);
35262
35283
  const prompt = preamble ? `${preamble}
@@ -35337,7 +35358,10 @@ ${body}
35337
35358
  }
35338
35359
  }
35339
35360
  }
35340
- } catch {}
35361
+ } catch (e) {
35362
+ log3(`preamble fetch failed (acpx): ${String(e)}`);
35363
+ await postBoundChatMessage(apiUrl, task, `⚠️ System: agent preamble fetch failed — running without role definition. Error: ${String(e).slice(0, 200)}`);
35364
+ }
35341
35365
  preamble += await buildPlanningPreamble(apiUrl, task);
35342
35366
  const issueContext = `## Issue ${task.key}: ${task.title}${task.description ? `
35343
35367
 
@@ -35354,6 +35378,20 @@ ${task.description}` : ""}`;
35354
35378
  ${cleanFollowup}`;
35355
35379
  } else {
35356
35380
  userPart = stripSelfMention(issueContext, preferType);
35381
+ userPart += `
35382
+
35383
+ ---
35384
+
35385
+ You have been assigned this issue. Investigate and resolve it. Read the codebase, understand the problem, implement a fix or complete the described work, and use your tools to make progress. When finished, emit a multi-plan action to mark the issue done (or blocked if you need human input).`;
35386
+ }
35387
+ if (issueHistoryBlock) {
35388
+ userPart += `
35389
+
35390
+ ---
35391
+
35392
+ ## Issue history (prior activity)
35393
+
35394
+ ${issueHistoryBlock}`;
35357
35395
  }
35358
35396
  if (attachmentRefs.length) {
35359
35397
  const lines = attachmentRefs.map((a) => `- ${a.filename}: ${a.path}`).join(`
@@ -35488,6 +35526,52 @@ ${userPart}` : userPart;
35488
35526
  }
35489
35527
  }
35490
35528
  }
35529
+ async function fetchIssueHistory(apiUrl, tenantWsId, projectId, issueId, log4) {
35530
+ const MAX_HISTORY_CHARS = 6000;
35531
+ try {
35532
+ const issueBase = `${apiUrl}/api/workspaces/${tenantWsId}/projects/${projectId}/issues/${issueId}`;
35533
+ const res = await apiClient.get(`${issueBase}/activity`);
35534
+ const activities = Array.isArray(res.data?.results) ? res.data.results : Array.isArray(res.data) ? res.data : [];
35535
+ if (!activities.length)
35536
+ return "";
35537
+ const lines = [];
35538
+ const recent = activities.slice(-20).reverse();
35539
+ for (const act of recent) {
35540
+ const action = act.action || "activity";
35541
+ const actor = act.actor_name || act.actor_id || "system";
35542
+ const ts = act.created_at ? new Date(act.created_at * 1000).toISOString().slice(0, 16) : "";
35543
+ let line = `- **${actor}** ${action}`;
35544
+ if (ts)
35545
+ line += ` _${ts}_`;
35546
+ if (act.details) {
35547
+ try {
35548
+ const d = typeof act.details === "string" ? JSON.parse(act.details) : act.details;
35549
+ if (d.message)
35550
+ line += `: ${String(d.message).slice(0, 200)}`;
35551
+ else if (d.title)
35552
+ line += `: ${String(d.title).slice(0, 200)}`;
35553
+ else if (d.from && d.to)
35554
+ line += `: ${d.from} → ${d.to}`;
35555
+ else if (d.text)
35556
+ line += `: ${String(d.text).slice(0, 200)}`;
35557
+ } catch {}
35558
+ }
35559
+ lines.push(line);
35560
+ }
35561
+ const block = lines.join(`
35562
+ `);
35563
+ if (block.length > MAX_HISTORY_CHARS) {
35564
+ log4(` issue history truncated: ${block.length} → ${MAX_HISTORY_CHARS} chars`);
35565
+ return block.slice(0, MAX_HISTORY_CHARS) + `
35566
+
35567
+ _… truncated_`;
35568
+ }
35569
+ return block;
35570
+ } catch (e) {
35571
+ log4(` issue history fetch failed: ${String(e).slice(0, 200)}`);
35572
+ return "";
35573
+ }
35574
+ }
35491
35575
  async function buildPlanningPreamble(apiUrl, task, _wsId) {
35492
35576
  const depth = typeof task.planning_depth === "number" ? task.planning_depth : 0;
35493
35577
  if (depth >= PLANNING_DEPTH_LIMIT) {
@@ -35686,7 +35770,7 @@ async function executePlanActions(apiUrl, parentTask, actions, ctx, parseErrors
35686
35770
  }
35687
35771
  }
35688
35772
  } else if (a.type === "delegate") {
35689
- const res = await apiClient.post(mutateUrl, { action: "update", id: a.id, assignee_type: "agent", assignee_id: a.assignee_id, status: "todo" }, { headers });
35773
+ const res = await apiClient.post(mutateUrl, { action: "update", id: a.id, assignee_type: "agent", assignee_id: a.assignee_id, status: "in_progress" }, { headers });
35690
35774
  if (!res.success) {
35691
35775
  lines.push(`- [err] delegate ${a.id}: ${res.error || res.status}`);
35692
35776
  results.push({ type: "delegate", status: "error", error: String(res.error || res.status), label: a.id });
@@ -37123,7 +37207,7 @@ async function executeChatPlanActions(actionsIn, parseErrors, ctx) {
37123
37207
  results.push({ type: "update", status: "ok", issue_id: res.data.id, key: res.data.key, status_to: a.status ?? null, title_to: a.title ?? null });
37124
37208
  tally(true);
37125
37209
  } else if (a.type === "delegate") {
37126
- const res = await apiClient.post(mutateUrl, { action: "update", id: a.id, assignee_type: "agent", assignee_id: a.assignee_id, status: "todo" }, { headers });
37210
+ const res = await apiClient.post(mutateUrl, { action: "update", id: a.id, assignee_type: "agent", assignee_id: a.assignee_id, status: "in_progress" }, { headers });
37127
37211
  if (!res.success) {
37128
37212
  lines.push(`- [err] delegate ${a.id}: ${res.error || res.status}`);
37129
37213
  results.push({ type: "delegate", status: "error", error: String(res.error || res.status), label: a.id });
@@ -38769,7 +38853,7 @@ import { parseArgs } from "util";
38769
38853
  // package.json
38770
38854
  var package_default = {
38771
38855
  name: "@shipers-dev/multi",
38772
- version: "0.64.0",
38856
+ version: "0.65.0",
38773
38857
  type: "module",
38774
38858
  bin: {
38775
38859
  "multi-agent": "./dist/index.js"
@@ -39850,6 +39934,66 @@ import { homedir as homedir4 } from "os";
39850
39934
  import { join as join16 } from "path";
39851
39935
  init_adapter_pidfile();
39852
39936
  init_errors();
39937
+ // package.json
39938
+ var package_default2 = {
39939
+ name: "@shipers-dev/multi",
39940
+ version: "0.65.0",
39941
+ type: "module",
39942
+ bin: {
39943
+ "multi-agent": "./dist/index.js"
39944
+ },
39945
+ files: [
39946
+ "dist"
39947
+ ],
39948
+ scripts: {
39949
+ dev: "MULTI_HOME=${MULTI_HOME:-$HOME/.multi-dev} MULTI_API=${MULTI_API:-http://127.0.0.1:8787} bun run src/index.ts connect",
39950
+ build: "bun build src/index.ts --outdir=dist --target=node --sourcemap=none --external undici --external loro-crdt && node scripts/post-build.cjs",
39951
+ prepublishOnly: "bun run build"
39952
+ },
39953
+ dependencies: {
39954
+ "@agentclientprotocol/claude-agent-acp": "^0.31.0",
39955
+ "@agentclientprotocol/sdk": "^0.20.0",
39956
+ "@effect/platform-node": "^0.103.0",
39957
+ effect: "^3.21.2",
39958
+ "loro-crdt": "^1.12.1",
39959
+ undici: "^7.0.0"
39960
+ },
39961
+ devDependencies: {
39962
+ "@multi/lib": "workspace:*"
39963
+ }
39964
+ };
39965
+
39966
+ // src/_impl/daemon-main.ts
39967
+ var CLI_VERSION = package_default2.version;
39968
+ function findBunBinary() {
39969
+ const tryPath = (p) => {
39970
+ try {
39971
+ return existsSync16(p) ? p : null;
39972
+ } catch {
39973
+ return null;
39974
+ }
39975
+ };
39976
+ if (process.execPath && process.execPath.endsWith("/bun")) {
39977
+ const hit = tryPath(process.execPath);
39978
+ if (hit)
39979
+ return hit;
39980
+ }
39981
+ const fromWhich = Bun.which?.("bun");
39982
+ if (fromWhich)
39983
+ return fromWhich;
39984
+ const candidates = [
39985
+ join16(homedir4(), ".bun", "bin", "bun"),
39986
+ "/opt/homebrew/bin/bun",
39987
+ "/usr/local/bin/bun",
39988
+ "/home/linuxbrew/.linuxbrew/bin/bun"
39989
+ ];
39990
+ for (const c of candidates) {
39991
+ const hit = tryPath(c);
39992
+ if (hit)
39993
+ return hit;
39994
+ }
39995
+ return null;
39996
+ }
39853
39997
  var MULTI_DIR5 = join16(homedir4(), ".multi");
39854
39998
  var PID_PATH2 = join16(MULTI_DIR5, "agent.pid");
39855
39999
  var PORT_PATH2 = join16(MULTI_DIR5, "agent.port");
@@ -40220,7 +40364,7 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
40220
40364
  }
40221
40365
  const route = () => {
40222
40366
  if (url2.pathname === "/health")
40223
- return Response.json({ ok: true, device_id: cfg.deviceId });
40367
+ return Response.json({ ok: true, device_id: cfg.deviceId, cli_version: CLI_VERSION });
40224
40368
  if (url2.pathname === "/files" && req.method === "GET") {
40225
40369
  if (req.headers.get("authorization") !== expectedAuth)
40226
40370
  return new Response("unauthorized", { status: 401 });
@@ -40627,6 +40771,47 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
40627
40771
  }
40628
40772
  })();
40629
40773
  }
40774
+ if (url2.pathname === "/upgrade" && req.method === "POST") {
40775
+ if (req.headers.get("authorization") !== expectedAuth)
40776
+ return new Response("unauthorized", { status: 401 });
40777
+ return (async () => {
40778
+ const bunBin = findBunBinary();
40779
+ if (!bunBin) {
40780
+ return Response.json({ error: "bun binary not found; run `bun add -g @shipers-dev/multi` manually" }, { status: 503 });
40781
+ }
40782
+ let body = {};
40783
+ try {
40784
+ body = await req.json();
40785
+ } catch {}
40786
+ const spec = body.target ? `@shipers-dev/multi@${body.target}` : "@shipers-dev/multi";
40787
+ log3(`[upgrade] spawning ${bunBin} add -g ${spec}`);
40788
+ const child = Bun.spawn([bunBin, "add", "-g", spec], {
40789
+ stdout: "pipe",
40790
+ stderr: "pipe",
40791
+ stdin: "ignore"
40792
+ });
40793
+ (async () => {
40794
+ try {
40795
+ const stdoutTxt = await new Response(child.stdout).text();
40796
+ const stderrTxt = await new Response(child.stderr).text();
40797
+ const code = await child.exited;
40798
+ if (stdoutTxt.trim())
40799
+ log3(`[upgrade] stdout: ${stdoutTxt.trim()}`);
40800
+ if (stderrTxt.trim())
40801
+ log3(`[upgrade] stderr: ${stderrTxt.trim()}`);
40802
+ if (code === 0) {
40803
+ log3("[upgrade] success; exiting so service manager respawns with new binary");
40804
+ setTimeout(() => process.exit(0), 500);
40805
+ } else {
40806
+ log3(`[upgrade] failed with exit code ${code}`);
40807
+ }
40808
+ } catch (e) {
40809
+ log3(`[upgrade] error: ${e.message}`);
40810
+ }
40811
+ })();
40812
+ return Response.json({ accepted: true, current_version: CLI_VERSION, target: spec }, { status: 202 });
40813
+ })();
40814
+ }
40630
40815
  if (url2.pathname === "/stop" && req.method === "POST") {
40631
40816
  if (req.headers.get("authorization") !== expectedAuth)
40632
40817
  return new Response("unauthorized", { status: 401 });
@@ -40742,7 +40927,8 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
40742
40927
  device_id: cfg.deviceId,
40743
40928
  workspace_id: cfg.workspaceId,
40744
40929
  api_url: apiUrl,
40745
- pid: process.pid
40930
+ pid: process.pid,
40931
+ cli_version: CLI_VERSION
40746
40932
  }, null, 2));
40747
40933
  } catch {}
40748
40934
  let tunnel;
@@ -41157,14 +41343,14 @@ var collectServiceEnv = () => {
41157
41343
  env.PATH = process.env.PATH;
41158
41344
  if (process.env.HOME)
41159
41345
  env.HOME = process.env.HOME;
41160
- if (process.env.MULTI_HOME)
41161
- env.MULTI_HOME = process.env.MULTI_HOME;
41162
- if (process.env.MULTI_API)
41163
- env.MULTI_API = process.env.MULTI_API;
41164
41346
  if (process.env.SHELL)
41165
41347
  env.SHELL = process.env.SHELL;
41166
41348
  if (process.env.LANG)
41167
41349
  env.LANG = process.env.LANG;
41350
+ for (const [k, v] of Object.entries(process.env)) {
41351
+ if (k.startsWith("MULTI_") && typeof v === "string" && v.length > 0)
41352
+ env[k] = v;
41353
+ }
41168
41354
  return env;
41169
41355
  };
41170
41356
  var ensureSupported = exports_Effect.fn("service.ensureSupported")(function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipers-dev/multi",
3
- "version": "0.64.0",
3
+ "version": "0.65.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "multi-agent": "./dist/index.js"