@shipers-dev/multi 0.48.0 → 0.49.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 +170 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33210,6 +33210,8 @@ async function runAcp(opts) {
|
|
|
33210
33210
|
let activeSessionId = opts.sessionId || null;
|
|
33211
33211
|
let recording = false;
|
|
33212
33212
|
let chunkCount = 0;
|
|
33213
|
+
let capturingSummary = false;
|
|
33214
|
+
let summaryBuf = "";
|
|
33213
33215
|
const alwaysAllow = new Set;
|
|
33214
33216
|
const client = {
|
|
33215
33217
|
async sessionUpdate(params) {
|
|
@@ -33304,7 +33306,23 @@ async function runAcp(opts) {
|
|
|
33304
33306
|
await opts.onEvent({ event_type: "error", payload: { message: `agent produced no output (stopReason=${stopReason})` } });
|
|
33305
33307
|
}
|
|
33306
33308
|
await opts.onEvent({ event_type: "result", payload: { stopReason } });
|
|
33307
|
-
|
|
33309
|
+
let summaryText;
|
|
33310
|
+
if (opts.summaryPrompt && chunkCount > 0) {
|
|
33311
|
+
try {
|
|
33312
|
+
capturingSummary = true;
|
|
33313
|
+
summaryBuf = "";
|
|
33314
|
+
await conn.prompt({
|
|
33315
|
+
sessionId: activeSessionId,
|
|
33316
|
+
prompt: [{ type: "text", text: opts.summaryPrompt }]
|
|
33317
|
+
});
|
|
33318
|
+
summaryText = summaryBuf.trim() || undefined;
|
|
33319
|
+
} catch (e) {
|
|
33320
|
+
await opts.onEvent({ event_type: "progress", payload: { message: `memory summary failed: ${fmtErr(e)}` } });
|
|
33321
|
+
} finally {
|
|
33322
|
+
capturingSummary = false;
|
|
33323
|
+
}
|
|
33324
|
+
}
|
|
33325
|
+
return { stopReason, sessionId: activeSessionId, summaryText };
|
|
33308
33326
|
} finally {
|
|
33309
33327
|
try {
|
|
33310
33328
|
child.kill();
|
|
@@ -33313,6 +33331,14 @@ async function runAcp(opts) {
|
|
|
33313
33331
|
async function handleSessionUpdate(params, o) {
|
|
33314
33332
|
const u = params.update;
|
|
33315
33333
|
const kind = u.sessionUpdate;
|
|
33334
|
+
if (capturingSummary) {
|
|
33335
|
+
if (kind === "agent_message_chunk") {
|
|
33336
|
+
const text = extractText(u.content);
|
|
33337
|
+
if (text)
|
|
33338
|
+
summaryBuf += text;
|
|
33339
|
+
}
|
|
33340
|
+
return;
|
|
33341
|
+
}
|
|
33316
33342
|
switch (kind) {
|
|
33317
33343
|
case "agent_message_chunk": {
|
|
33318
33344
|
const text = extractText(u.content);
|
|
@@ -33464,6 +33490,94 @@ function dlog(msg) {
|
|
|
33464
33490
|
`);
|
|
33465
33491
|
} catch {}
|
|
33466
33492
|
}
|
|
33493
|
+
async function spawnAcpxPrompt(agentType, cwd, sessionName, prompt, collectAssistantText, forward, onPlanUpdate) {
|
|
33494
|
+
const args2 = ["acpx", "--format", "json", "--json-strict", "--approve-all", "--ttl", "0"];
|
|
33495
|
+
if (cwd)
|
|
33496
|
+
args2.push("--cwd", cwd);
|
|
33497
|
+
args2.push(agentType);
|
|
33498
|
+
if (sessionName)
|
|
33499
|
+
args2.push("prompt", "-s", sessionName);
|
|
33500
|
+
else
|
|
33501
|
+
args2.push("prompt");
|
|
33502
|
+
args2.push(prompt);
|
|
33503
|
+
dlog(`[acpx] prompt: ${args2.slice(0, 10).join(" ")} ... (prompt len=${prompt.length})`);
|
|
33504
|
+
const proc = Bun.spawn(args2, { stdout: "pipe", stderr: "pipe", stdin: "ignore" });
|
|
33505
|
+
let stopReason = "end_turn";
|
|
33506
|
+
(async () => {
|
|
33507
|
+
try {
|
|
33508
|
+
const r = proc.stderr.getReader();
|
|
33509
|
+
const d = new TextDecoder;
|
|
33510
|
+
let sb = "";
|
|
33511
|
+
while (true) {
|
|
33512
|
+
const { value: value3, done: done9 } = await r.read();
|
|
33513
|
+
if (done9)
|
|
33514
|
+
break;
|
|
33515
|
+
sb += d.decode(value3, { stream: true });
|
|
33516
|
+
let nl;
|
|
33517
|
+
while ((nl = sb.indexOf(`
|
|
33518
|
+
`)) !== -1) {
|
|
33519
|
+
const ln = sb.slice(0, nl).trim();
|
|
33520
|
+
sb = sb.slice(nl + 1);
|
|
33521
|
+
if (ln)
|
|
33522
|
+
dlog(`[acpx stderr] ${ln.slice(0, 500)}`);
|
|
33523
|
+
}
|
|
33524
|
+
}
|
|
33525
|
+
} catch {}
|
|
33526
|
+
})();
|
|
33527
|
+
const fakeOpts = { agentType, prompt, cwd, sessionName, onEvent: () => {}, onPlanUpdate };
|
|
33528
|
+
const reader = proc.stdout.getReader();
|
|
33529
|
+
const dec = new TextDecoder;
|
|
33530
|
+
let buf = "";
|
|
33531
|
+
while (true) {
|
|
33532
|
+
const { value: value3, done: done9 } = await reader.read();
|
|
33533
|
+
if (done9)
|
|
33534
|
+
break;
|
|
33535
|
+
buf += dec.decode(value3, { stream: true });
|
|
33536
|
+
let nl;
|
|
33537
|
+
while ((nl = buf.indexOf(`
|
|
33538
|
+
`)) !== -1) {
|
|
33539
|
+
const line = buf.slice(0, nl).trim();
|
|
33540
|
+
buf = buf.slice(nl + 1);
|
|
33541
|
+
if (!line)
|
|
33542
|
+
continue;
|
|
33543
|
+
dlog(`[acpx stdout] ${line.slice(0, 500)}`);
|
|
33544
|
+
const events = parseAcpLine(line, (r) => {
|
|
33545
|
+
stopReason = r;
|
|
33546
|
+
}, fakeOpts);
|
|
33547
|
+
for (const ev of events) {
|
|
33548
|
+
if (collectAssistantText && ev.event_type === "assistant_text") {
|
|
33549
|
+
const t = ev.payload?.text;
|
|
33550
|
+
if (typeof t === "string")
|
|
33551
|
+
collectAssistantText.buf += t;
|
|
33552
|
+
} else {
|
|
33553
|
+
await forward(ev);
|
|
33554
|
+
}
|
|
33555
|
+
}
|
|
33556
|
+
}
|
|
33557
|
+
}
|
|
33558
|
+
if (buf.trim()) {
|
|
33559
|
+
const events = parseAcpLine(buf.trim(), (r) => {
|
|
33560
|
+
stopReason = r;
|
|
33561
|
+
}, fakeOpts);
|
|
33562
|
+
for (const ev of events) {
|
|
33563
|
+
if (collectAssistantText && ev.event_type === "assistant_text") {
|
|
33564
|
+
const t = ev.payload?.text;
|
|
33565
|
+
if (typeof t === "string")
|
|
33566
|
+
collectAssistantText.buf += t;
|
|
33567
|
+
} else {
|
|
33568
|
+
await forward(ev);
|
|
33569
|
+
}
|
|
33570
|
+
}
|
|
33571
|
+
}
|
|
33572
|
+
const code = await proc.exited;
|
|
33573
|
+
if (code !== 0 && code !== 130) {
|
|
33574
|
+
if (!collectAssistantText)
|
|
33575
|
+
await forward({ event_type: "error", payload: { message: `acpx exited with code ${code}` } });
|
|
33576
|
+
else
|
|
33577
|
+
dlog(`[acpx] summary spawn exit=${code}`);
|
|
33578
|
+
}
|
|
33579
|
+
return { stopReason };
|
|
33580
|
+
}
|
|
33467
33581
|
async function runAcpx(opts) {
|
|
33468
33582
|
const args2 = ["acpx", "--format", "json", "--json-strict", "--approve-all", "--ttl", "0"];
|
|
33469
33583
|
if (opts.cwd)
|
|
@@ -33550,7 +33664,17 @@ async function runAcpx(opts) {
|
|
|
33550
33664
|
if (code !== 0 && code !== 130) {
|
|
33551
33665
|
await opts.onEvent({ event_type: "error", payload: { message: `acpx exited with code ${code}` } });
|
|
33552
33666
|
}
|
|
33553
|
-
|
|
33667
|
+
let summaryText;
|
|
33668
|
+
if (opts.summaryPrompt && opts.sessionName && (code === 0 || code === 130)) {
|
|
33669
|
+
const collect = { buf: "" };
|
|
33670
|
+
try {
|
|
33671
|
+
await spawnAcpxPrompt(opts.agentType, opts.cwd, opts.sessionName, opts.summaryPrompt, collect, () => {}, opts.onPlanUpdate);
|
|
33672
|
+
summaryText = collect.buf.trim() || undefined;
|
|
33673
|
+
} catch (e) {
|
|
33674
|
+
dlog(`[acpx] summary error: ${String(e)}`);
|
|
33675
|
+
}
|
|
33676
|
+
}
|
|
33677
|
+
return { stopReason, summaryText };
|
|
33554
33678
|
}
|
|
33555
33679
|
function parseAcpLine(line, setStop, opts) {
|
|
33556
33680
|
let msg;
|
|
@@ -34085,7 +34209,6 @@ var init_chat = __esm(() => {
|
|
|
34085
34209
|
runtime: exports_external.string().nullable().optional()
|
|
34086
34210
|
});
|
|
34087
34211
|
});
|
|
34088
|
-
|
|
34089
34212
|
// ../lib/index.ts
|
|
34090
34213
|
var init_lib = __esm(() => {
|
|
34091
34214
|
init_streams();
|
|
@@ -34431,6 +34554,38 @@ function resolveEnforcementMode(task) {
|
|
|
34431
34554
|
return m;
|
|
34432
34555
|
return "enforce";
|
|
34433
34556
|
}
|
|
34557
|
+
function buildMemorySummaryPrompt(task) {
|
|
34558
|
+
const key = task?.key || task?.issue_id || "task";
|
|
34559
|
+
const title = task?.title || "";
|
|
34560
|
+
return [
|
|
34561
|
+
`The task "${key}: ${title}" is now complete.`,
|
|
34562
|
+
`Provide a memory summary for future agents working on this project.`,
|
|
34563
|
+
`Output EXACTLY 5 short bullets (one line each, prefix "- "):`,
|
|
34564
|
+
`1. What changed (the actual outcome)`,
|
|
34565
|
+
`2. Why (motivation, constraints)`,
|
|
34566
|
+
`3. Key decisions / tradeoffs`,
|
|
34567
|
+
`4. Files or modules touched`,
|
|
34568
|
+
`5. Open follow-ups, if any`,
|
|
34569
|
+
``,
|
|
34570
|
+
`Rules: do NOT call any tools. Reply with ONLY the 5 bullets, no preamble, no closing.`
|
|
34571
|
+
].join(`
|
|
34572
|
+
`);
|
|
34573
|
+
}
|
|
34574
|
+
async function writeTaskMemory(apiUrl, task, text) {
|
|
34575
|
+
if (!task?.project_id || !text)
|
|
34576
|
+
return;
|
|
34577
|
+
try {
|
|
34578
|
+
await apiClient.post(`${apiUrl}/api/memory/write`, {
|
|
34579
|
+
project_id: task.project_id,
|
|
34580
|
+
source_kind: "task",
|
|
34581
|
+
source_id: String(task.issue_id || task.id || ""),
|
|
34582
|
+
agent_id: task.agent_id ?? null,
|
|
34583
|
+
text: text.slice(0, 19500)
|
|
34584
|
+
});
|
|
34585
|
+
} catch (e) {
|
|
34586
|
+
log3(`memory write failed: ${String(e)}`);
|
|
34587
|
+
}
|
|
34588
|
+
}
|
|
34434
34589
|
async function handleRunTask(apiUrl, deviceId, task, detected, ctx) {
|
|
34435
34590
|
const issueId = task.issue_id;
|
|
34436
34591
|
const isFollowup = !!task.followup;
|
|
@@ -34513,6 +34668,7 @@ async function handleRunTask(apiUrl, deviceId, task, detected, ctx) {
|
|
|
34513
34668
|
let hadError = false;
|
|
34514
34669
|
let hasAssistantText = false;
|
|
34515
34670
|
let liveCommentPromise = null;
|
|
34671
|
+
let memorySummary = null;
|
|
34516
34672
|
const ensureLiveComment = () => {
|
|
34517
34673
|
if (liveCommentId)
|
|
34518
34674
|
return Promise.resolve();
|
|
@@ -34821,7 +34977,7 @@ ${userPart}` : userPart;
|
|
|
34821
34977
|
throw new Error(`ACP adapter for ${chosen.type} not found`);
|
|
34822
34978
|
log3(` adapter: ${chosen.type} → ${adapterBin.join(" ")}`);
|
|
34823
34979
|
const startedAt = Date.now();
|
|
34824
|
-
const { sessionId } = await runAcp({
|
|
34980
|
+
const { sessionId, summaryText: acpSummary } = await runAcp({
|
|
34825
34981
|
apiUrl,
|
|
34826
34982
|
issueId,
|
|
34827
34983
|
deviceId,
|
|
@@ -34833,6 +34989,7 @@ ${userPart}` : userPart;
|
|
|
34833
34989
|
adapterBin,
|
|
34834
34990
|
autonomy: task.autonomy_level,
|
|
34835
34991
|
cwd: workingDir,
|
|
34992
|
+
summaryPrompt: buildMemorySummaryPrompt(task),
|
|
34836
34993
|
onEvent: eventHandler,
|
|
34837
34994
|
onSession: async (sid) => {
|
|
34838
34995
|
try {
|
|
@@ -34857,6 +35014,7 @@ ${userPart}` : userPart;
|
|
|
34857
35014
|
});
|
|
34858
35015
|
postStream(apiUrl, issueId, "run_finished", { stopReason: typeof sessionId === "string" ? "ok" : "unknown", duration_ms: Date.now() - startedAt });
|
|
34859
35016
|
log3(` acp session ${sessionId.slice(0, 8)}`);
|
|
35017
|
+
memorySummary = acpSummary || null;
|
|
34860
35018
|
} else if (useAcpx) {
|
|
34861
35019
|
let preamble = "";
|
|
34862
35020
|
try {
|
|
@@ -34926,11 +35084,12 @@ Write generated files to: ${outDir}`;
|
|
|
34926
35084
|
${userPart}` : userPart;
|
|
34927
35085
|
log3(` acpx runner: ${preferType}`);
|
|
34928
35086
|
const acpxStartedAt = Date.now();
|
|
34929
|
-
await runAcpx({
|
|
35087
|
+
const { summaryText: acpxSummary } = await runAcpx({
|
|
34930
35088
|
agentType: preferType,
|
|
34931
35089
|
prompt: full,
|
|
34932
35090
|
cwd: workingDir,
|
|
34933
35091
|
sessionName: `issue-${issueId}`,
|
|
35092
|
+
summaryPrompt: buildMemorySummaryPrompt(task),
|
|
34934
35093
|
onEvent: eventHandler,
|
|
34935
35094
|
onSpawn: (child) => {
|
|
34936
35095
|
if (ctx?.runEntry) {
|
|
@@ -34941,6 +35100,7 @@ ${userPart}` : userPart;
|
|
|
34941
35100
|
}
|
|
34942
35101
|
});
|
|
34943
35102
|
postStream(apiUrl, issueId, "run_finished", { stopReason: "ok", duration_ms: Date.now() - acpxStartedAt });
|
|
35103
|
+
memorySummary = acpxSummary || null;
|
|
34944
35104
|
} else {
|
|
34945
35105
|
const runner = pickRunner(detected, preferType);
|
|
34946
35106
|
for await (const event of runner(task))
|
|
@@ -34984,6 +35144,10 @@ ${userPart}` : userPart;
|
|
|
34984
35144
|
if (ISSUE_BASE)
|
|
34985
35145
|
await apiClient.post(`${ISSUE_BASE}/complete`, {});
|
|
34986
35146
|
log3(` ✓ ${task.key} complete`);
|
|
35147
|
+
if (memorySummary) {
|
|
35148
|
+
await writeTaskMemory(apiUrl, task, memorySummary);
|
|
35149
|
+
log3(` \uD83D\uDCDD memory summary written (${memorySummary.length} chars)`);
|
|
35150
|
+
}
|
|
34987
35151
|
if (baseWorkingDir) {
|
|
34988
35152
|
const mergeTarget = task.merge_to_main === true || task.merge_to_main === "true" ? "main" : typeof task.merge_target === "string" ? task.merge_target : null;
|
|
34989
35153
|
await exports_Effect.runPromise(terminalGitFlowE(apiUrl, issueId, baseWorkingDir, task.key || issueId, resolveEnforcementMode(task), mergeTarget));
|
|
@@ -37798,7 +37962,7 @@ import { parseArgs } from "util";
|
|
|
37798
37962
|
// package.json
|
|
37799
37963
|
var package_default = {
|
|
37800
37964
|
name: "@shipers-dev/multi",
|
|
37801
|
-
version: "0.
|
|
37965
|
+
version: "0.49.0",
|
|
37802
37966
|
type: "module",
|
|
37803
37967
|
bin: {
|
|
37804
37968
|
"multi-agent": "./dist/index.js"
|