spacecode 2.11.0 → 2.12.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 +2 -2
- package/dist/index.js.map +1 -1
- package/dist/repl.mjs +39 -18
- package/package.json +1 -1
package/dist/repl.mjs
CHANGED
|
@@ -4088,7 +4088,7 @@ var init_defaults_core_loop = __esm({
|
|
|
4088
4088
|
body: `You are the core of a coding run. You direct; you never read files, search, run commands or write code. Workers do that.
|
|
4089
4089
|
|
|
4090
4090
|
## Tools (the only ones that exist)
|
|
4091
|
-
- edit_plan {tasks}: add tasks. Each task has id, role, goal (what the worker must do, concrete and self-contained: the worker sees only this, the files and the acceptance), files (a file belongs to one open task at a time), acceptance {kind: checks|rubric|none, detail}, budgetTokens, deadlineMs.
|
|
4091
|
+
- edit_plan {tasks}: add tasks. Each task has id, role, goal (what the worker must do, concrete and self-contained: the worker sees only this, the files and the acceptance), files (a file belongs to one open task at a time), acceptance {kind: checks|rubric|none, detail} (a code task that the repo can check, with tests or a typecheck, uses kind checks so the repo's own typecheck and tests must pass; rubric and none skip them), budgetTokens, deadlineMs (milliseconds; a worker needs minutes to clone, work and check, so use 600000 for a code task; values are clamped to 600000-840000).
|
|
4092
4092
|
- dispatch_task {taskId}: start a pending task (at most 2 attempts per task).
|
|
4093
4093
|
- accept_task {taskId}: accept a task in review. A checks task needs a passing verifier first.
|
|
4094
4094
|
- drop_task {taskId, reason}: drop a task you no longer need.
|
|
@@ -4101,7 +4101,7 @@ var init_defaults_core_loop = __esm({
|
|
|
4101
4101
|
- When the token budget is exhausted, package what is accepted and send the result.
|
|
4102
4102
|
- Prefer small tasks with one owner per file. Stay inside the budget.
|
|
4103
4103
|
- You are only woken by worker results and user messages, never by your own actions. So in the same answer as edit_plan, dispatch_task every task that can start now; a task left pending with nothing running stalls the run.
|
|
4104
|
-
- Tasks that must follow another (a test after its implementation) are dispatched after the earlier one is accepted.
|
|
4104
|
+
- Tasks that must follow another (a test after its implementation) are dispatched after the earlier one is accepted; the later worker starts from the accepted work, so give it only its own files and do not repeat the earlier task's work.
|
|
4105
4105
|
- Worker digests are already included in what you are shown; there is nothing more to read. Decide from them (accept_task, drop_task, or dispatch a retry).
|
|
4106
4106
|
- Make every tool call of your answer at once; you do not see results between calls. Reply with tool calls only.`
|
|
4107
4107
|
},
|
|
@@ -104407,7 +104407,9 @@ async function runCoreTurn(d, prompt) {
|
|
|
104407
104407
|
let lastText = "";
|
|
104408
104408
|
const say = (content) => {
|
|
104409
104409
|
lastText = content;
|
|
104410
|
-
d.emit({ type: "text", seq: ++seq, content }
|
|
104410
|
+
d.emit({ type: "text", seq: ++seq, content: `${content}
|
|
104411
|
+
|
|
104412
|
+
` });
|
|
104411
104413
|
};
|
|
104412
104414
|
const call = async (method, path13, body) => {
|
|
104413
104415
|
const res = await d.fetch(`${d.backendUrl}/api/core/runs${path13}`, {
|
|
@@ -104424,7 +104426,7 @@ async function runCoreTurn(d, prompt) {
|
|
|
104424
104426
|
const created = await call("POST", "", {
|
|
104425
104427
|
sessionId: d.sessionId,
|
|
104426
104428
|
budgetTokens: d.budgetTokens ?? DEFAULT_BUDGET,
|
|
104427
|
-
...d.repo ? { repoUrl: d.repo.url, ...d.repo.branch ? { branch: d.repo.branch } : {} } : {}
|
|
104429
|
+
...d.repo ? { repoUrl: d.repo.url, ...d.repo.branch ? { branch: d.repo.branch } : {}, ...d.ghToken ? { ghToken: d.ghToken } : {} } : {}
|
|
104428
104430
|
});
|
|
104429
104431
|
runId = created.runId;
|
|
104430
104432
|
d.setRunId(runId);
|
|
@@ -104433,23 +104435,21 @@ async function runCoreTurn(d, prompt) {
|
|
|
104433
104435
|
const chat = await call("POST", `/${runId}/chat`, { id: d.newId(), text: prompt });
|
|
104434
104436
|
if (chat.reply) say(chat.reply.text);
|
|
104435
104437
|
else if (chat.replyError) say("(The quick reply is unavailable; the planner has your message.)");
|
|
104436
|
-
|
|
104437
|
-
let known = {};
|
|
104438
|
-
const patches = /* @__PURE__ */ new Map();
|
|
104438
|
+
const cur = d.cursor;
|
|
104439
104439
|
const startedAt = d.now();
|
|
104440
104440
|
const maxWait = d.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;
|
|
104441
104441
|
let delay = 1e3;
|
|
104442
104442
|
let state = null;
|
|
104443
104443
|
for (; ; ) {
|
|
104444
104444
|
if (d.signal.aborted) throw new CoreTurnError("aborted");
|
|
104445
|
-
const events = (await call("GET", `/${runId}/events?after=${after}&limit=200`)).events;
|
|
104445
|
+
const events = (await call("GET", `/${runId}/events?after=${cur.after}&limit=200`)).events;
|
|
104446
104446
|
for (const e of events) {
|
|
104447
|
-
after = Math.max(after, e.seq);
|
|
104447
|
+
cur.after = Math.max(cur.after, e.seq);
|
|
104448
104448
|
if (!e.applied) continue;
|
|
104449
104449
|
const p = e.payload ?? {};
|
|
104450
104450
|
if (e.type === "core_message" && typeof p.text === "string") say(p.text);
|
|
104451
104451
|
if (e.type === "worker_done" && typeof p.taskId === "string") {
|
|
104452
|
-
if (typeof p.patchKey === "string") patches
|
|
104452
|
+
if (typeof p.patchKey === "string") cur.patches[p.taskId] = p.patchKey;
|
|
104453
104453
|
if (Array.isArray(p.problems) || typeof p.reason === "string") say(`task ${p.taskId}: the worker's attempt failed${typeof p.reason === "string" ? ` (${p.reason})` : ""}`);
|
|
104454
104454
|
}
|
|
104455
104455
|
if (e.type === "verifier_result" && Array.isArray(p.problems) && p.problems.length > 0) say(`task ${String(p.taskId)}: verifier: ${p.problems.join("; ")}`);
|
|
@@ -104457,9 +104457,9 @@ async function runCoreTurn(d, prompt) {
|
|
|
104457
104457
|
state = (await call("GET", `/${runId}`)).state;
|
|
104458
104458
|
const now = {};
|
|
104459
104459
|
for (const id of state.order) now[id] = state.tasks[id].status;
|
|
104460
|
-
const changed = state.order.filter((id) => now[id] !== known[id]);
|
|
104460
|
+
const changed = state.order.filter((id) => now[id] !== cur.known[id]);
|
|
104461
104461
|
if (changed.length > 0) say(changed.map((id) => `task ${id}: ${now[id]}`).join(", "));
|
|
104462
|
-
known = now;
|
|
104462
|
+
cur.known = now;
|
|
104463
104463
|
if (TERMINAL.has(state.status)) break;
|
|
104464
104464
|
if (d.now() - startedAt > maxWait) throw new CoreTurnError("the run is still working after the wait limit; ask again later or check on it");
|
|
104465
104465
|
delay = events.length > 0 ? 1e3 : Math.min(5e3, Math.round(delay * 1.5));
|
|
@@ -104467,22 +104467,29 @@ async function runCoreTurn(d, prompt) {
|
|
|
104467
104467
|
}
|
|
104468
104468
|
if (state.status === "core_failed") say('The planner failed on every model route. Say "continue" after a moment to resume this run.');
|
|
104469
104469
|
if (state.status === "stopped") say("The run stopped before everything was accepted.");
|
|
104470
|
+
let patchFailures = 0;
|
|
104470
104471
|
for (const id of state.order) {
|
|
104471
|
-
if (state.tasks[id].status !== "accepted" || !patches.
|
|
104472
|
+
if (state.tasks[id].status !== "accepted" || !(id in cur.patches) || cur.applied.includes(id)) continue;
|
|
104472
104473
|
const art = await d.fetch(`${d.backendUrl}/api/core/runs/${runId}/artifacts/${encodeURIComponent(id)}`, { headers: { authorization: `Bearer ${d.apiKey}` }, signal: d.signal });
|
|
104473
104474
|
if (!art.ok) {
|
|
104474
104475
|
say(`task ${id}: its patch could not be fetched (${art.status})`);
|
|
104476
|
+
patchFailures++;
|
|
104475
104477
|
continue;
|
|
104476
104478
|
}
|
|
104479
|
+
cur.applied.push(id);
|
|
104477
104480
|
const result = await d.applyPatch({ taskId: id, patch: await art.text() });
|
|
104478
104481
|
say(`task ${id}: ${result.applied ? "patch applied" : "patch not applied"} (${result.detail})`);
|
|
104482
|
+
if (!result.applied) patchFailures++;
|
|
104479
104483
|
}
|
|
104480
|
-
|
|
104484
|
+
const tokens = Math.max(0, state.tokensUsed - cur.tokensBase);
|
|
104485
|
+
cur.tokensBase = state.tokensUsed;
|
|
104486
|
+
return { success: (state.status === "done" || state.status === "needs_user") && patchFailures === 0, text: lastText, tokens };
|
|
104481
104487
|
}
|
|
104482
|
-
var TERMINAL, DEFAULT_BUDGET, DEFAULT_MAX_WAIT_MS, CoreTurnError;
|
|
104488
|
+
var newRunCursor, TERMINAL, DEFAULT_BUDGET, DEFAULT_MAX_WAIT_MS, CoreTurnError;
|
|
104483
104489
|
var init_core_turn = __esm({
|
|
104484
104490
|
"src/core/core-turn.ts"() {
|
|
104485
104491
|
"use strict";
|
|
104492
|
+
newRunCursor = () => ({ after: 0, known: {}, patches: {}, applied: [], tokensBase: 0 });
|
|
104486
104493
|
TERMINAL = /* @__PURE__ */ new Set(["done", "stopped", "core_failed", "needs_user"]);
|
|
104487
104494
|
DEFAULT_BUDGET = 5e5;
|
|
104488
104495
|
DEFAULT_MAX_WAIT_MS = 30 * 6e4;
|
|
@@ -104512,13 +104519,20 @@ async function detectRepo(cwd2, env3) {
|
|
|
104512
104519
|
const branch = env3.SPACECODE_CORE_BRANCH?.trim();
|
|
104513
104520
|
return { url, ...branch ? { branch } : {} };
|
|
104514
104521
|
}
|
|
104522
|
+
function shareableGithubToken(env3) {
|
|
104523
|
+
if (env3.SPACECODE_CORE_SHARE_GITHUB_TOKEN?.trim().toLowerCase() !== "on") return void 0;
|
|
104524
|
+
const t = (env3.GH_TOKEN ?? env3.GITHUB_TOKEN)?.trim();
|
|
104525
|
+
return t ? t : void 0;
|
|
104526
|
+
}
|
|
104515
104527
|
function patchApplier(executor, cwd2, runId) {
|
|
104516
104528
|
return async ({ taskId, patch }) => {
|
|
104517
|
-
const
|
|
104529
|
+
const top = await gitOut(cwd2, ["rev-parse", "--show-toplevel"]) ?? cwd2;
|
|
104530
|
+
const gitDir = await gitOut(cwd2, ["rev-parse", "--absolute-git-dir"]) ?? join14(top, ".git");
|
|
104531
|
+
const dir = join14(gitDir, "spacecode-core", runId.replace(/[^A-Za-z0-9_.-]/g, "_"));
|
|
104518
104532
|
await mkdir6(dir, { recursive: true });
|
|
104519
104533
|
const file = join14(dir, `${taskId.replace(/[^A-Za-z0-9_.-]/g, "_")}.patch`);
|
|
104520
104534
|
await writeFile6(file, patch, { encoding: "utf-8", mode: 384 });
|
|
104521
|
-
const res = await executor.execute("bash", { command: `git apply --3way ${
|
|
104535
|
+
const res = await executor.execute("bash", { command: `git -C ${sq(top)} apply --3way ${sq(file)}` });
|
|
104522
104536
|
return res.is_error ? { applied: false, detail: `${res.output.slice(0, 200).trim() || "git apply failed"}; patch kept at ${file}` } : { applied: true, detail: "git apply --3way" };
|
|
104523
104537
|
};
|
|
104524
104538
|
}
|
|
@@ -104532,11 +104546,13 @@ async function runCoreForRepl(o) {
|
|
|
104532
104546
|
apiKey: o.apiKey,
|
|
104533
104547
|
sessionId: o.sessionId,
|
|
104534
104548
|
repo,
|
|
104549
|
+
...shareableGithubToken(o.env) ? { ghToken: shareableGithubToken(o.env) } : {},
|
|
104535
104550
|
getRunId: () => o.runIdRef.current,
|
|
104536
104551
|
setRunId: (id) => {
|
|
104537
104552
|
o.runIdRef.current = id;
|
|
104538
104553
|
runIdForApply = id;
|
|
104539
104554
|
},
|
|
104555
|
+
cursor: o.cursor,
|
|
104540
104556
|
newId: () => crypto.randomUUID(),
|
|
104541
104557
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
104542
104558
|
now: Date.now,
|
|
@@ -104549,13 +104565,14 @@ async function runCoreForRepl(o) {
|
|
|
104549
104565
|
const messages = [...o.history, { role: "user", content: o.prompt }, { role: "assistant", content: [{ type: "text", text: turn.text || "(no reply)" }] }];
|
|
104550
104566
|
return { success: turn.success, messages, totalInputTokens: turn.tokens, totalOutputTokens: 0 };
|
|
104551
104567
|
}
|
|
104552
|
-
var coreModeOn;
|
|
104568
|
+
var coreModeOn, sq;
|
|
104553
104569
|
var init_core_mode = __esm({
|
|
104554
104570
|
"src/core/core-mode.ts"() {
|
|
104555
104571
|
"use strict";
|
|
104556
104572
|
init_core_turn();
|
|
104557
104573
|
init_profile_runtime();
|
|
104558
104574
|
coreModeOn = (env3) => env3.SPACECODE_CORE?.trim().toLowerCase() === "on";
|
|
104575
|
+
sq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
104559
104576
|
}
|
|
104560
104577
|
});
|
|
104561
104578
|
|
|
@@ -104594,6 +104611,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
104594
104611
|
}, [projectRoot]);
|
|
104595
104612
|
const sessionIdRef = (0, import_react30.useRef)(null);
|
|
104596
104613
|
const coreRunIdRef = (0, import_react30.useRef)(null);
|
|
104614
|
+
const coreCursorRef = (0, import_react30.useRef)(newRunCursor());
|
|
104597
104615
|
const sessionCreatedAtRef = (0, import_react30.useRef)(null);
|
|
104598
104616
|
const sessionTokensRef = (0, import_react30.useRef)({ input: 0, output: 0 });
|
|
104599
104617
|
const profilePromiseRef = (0, import_react30.useRef)(null);
|
|
@@ -104841,6 +104859,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
|
|
|
104841
104859
|
sessionIdRef.current = null;
|
|
104842
104860
|
sessionCreatedAtRef.current = null;
|
|
104843
104861
|
coreRunIdRef.current = null;
|
|
104862
|
+
coreCursorRef.current = newRunCursor();
|
|
104844
104863
|
return;
|
|
104845
104864
|
case "help":
|
|
104846
104865
|
notice(formatHelp());
|
|
@@ -105152,6 +105171,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
|
|
|
105152
105171
|
sessionId: sessionIdRef.current ??= sessions ? sessions.newId() : crypto.randomUUID(),
|
|
105153
105172
|
cwd: projectRoot,
|
|
105154
105173
|
runIdRef: coreRunIdRef,
|
|
105174
|
+
cursor: coreCursorRef.current,
|
|
105155
105175
|
signal: controller.signal,
|
|
105156
105176
|
emit,
|
|
105157
105177
|
executor: toolset.executor,
|
|
@@ -105270,6 +105290,7 @@ var init_useReplController = __esm({
|
|
|
105270
105290
|
init_src();
|
|
105271
105291
|
init_command_loader();
|
|
105272
105292
|
init_core_mode();
|
|
105293
|
+
init_core_turn();
|
|
105273
105294
|
init_slash_commands();
|
|
105274
105295
|
init_useEventAccumulator();
|
|
105275
105296
|
gitTopLevel = () => new Promise((resolve2) => {
|