@sideboard-ai/core 0.1.73 → 0.1.74

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 (26) hide show
  1. package/dist/agents/cursor-runner.cjs +1 -1
  2. package/dist/agents/cursor-runner.js +1 -1
  3. package/dist/{agents-77GE7VRW.js → agents-2S2JVLZ6.js} +3 -3
  4. package/dist/{agents-GUFUYXKP.js → agents-H7M5CQY2.js} +4 -4
  5. package/dist/{chunk-POW7JCB5.js → chunk-2NKSHIRI.js} +1 -1
  6. package/dist/{chunk-WSFZPOPH.js → chunk-5KJMNNCB.js} +17 -11
  7. package/dist/{chunk-NF6Y4GTE.js → chunk-77W62MTX.js} +1 -1
  8. package/dist/{chunk-CYKPUTXN.js → chunk-DG3S2UXP.js} +1 -1
  9. package/dist/{chunk-CXT2PLO7.js → chunk-GJ2LZQJI.js} +1 -1
  10. package/dist/{chunk-V4ACEJX2.js → chunk-HCWAIEBU.js} +11 -10
  11. package/dist/{chunk-AFW3M6LU.js → chunk-KBKPVKYZ.js} +17 -11
  12. package/dist/{chunk-3WJAUKIL.js → chunk-SEOICVGB.js} +1 -1
  13. package/dist/{chunk-6RFPKZNC.js → chunk-XA2FQJTN.js} +11 -10
  14. package/dist/{coordinator-prompt-4U2QNHGT.js → coordinator-prompt-3XXSG7M2.js} +1 -1
  15. package/dist/{coordinator-prompt-ZHBDHMZB.js → coordinator-prompt-RTUCCPCI.js} +1 -1
  16. package/dist/{global-workspace-S3B6ESZS.js → global-workspace-CSIS62Z4.js} +2 -2
  17. package/dist/{global-workspace-VF56FTPY.js → global-workspace-PJU6HISJ.js} +2 -2
  18. package/dist/index.cjs +155 -32
  19. package/dist/index.d.cts +34 -3
  20. package/dist/index.d.ts +34 -3
  21. package/dist/index.js +134 -23
  22. package/dist/mcp/run-stdio.cjs +141 -32
  23. package/dist/mcp/run-stdio.js +124 -22
  24. package/dist/{workspaces-ZJ45O4CD.js → workspaces-L4TXMUNM.js} +3 -3
  25. package/dist/{workspaces-R66324S7.js → workspaces-W72KZL4B.js} +3 -3
  26. package/package.json +1 -1
@@ -16,14 +16,14 @@ import {
16
16
  resolveQuotaFallbackAgent,
17
17
  sideboardMcpProfile,
18
18
  summarizeTurnStderr
19
- } from "../chunk-AFW3M6LU.js";
19
+ } from "../chunk-KBKPVKYZ.js";
20
20
  import "../chunk-VROPG6QF.js";
21
21
  import {
22
22
  addWorkspace,
23
23
  ensureWorkspace,
24
24
  removeWorkspace,
25
25
  syncWorkspacesFromThreads
26
- } from "../chunk-CXT2PLO7.js";
26
+ } from "../chunk-GJ2LZQJI.js";
27
27
  import {
28
28
  extractPresentedPlan,
29
29
  readPlanFile,
@@ -38,14 +38,14 @@ import {
38
38
  isGlobalThread,
39
39
  isOrchestratorThread,
40
40
  orchestratorSessionPoisonedByBuiltins
41
- } from "../chunk-POW7JCB5.js";
41
+ } from "../chunk-2NKSHIRI.js";
42
42
  import {
43
43
  SLACK_REPLY_FORMATTING,
44
44
  coordinatorSystemPrompt,
45
45
  coordinatorTurnReminder,
46
46
  enrichWorkspacesWithGithub,
47
47
  ensureGlobalCoordinatorCwd
48
- } from "../chunk-6RFPKZNC.js";
48
+ } from "../chunk-XA2FQJTN.js";
49
49
  import {
50
50
  addPrStackLayer,
51
51
  allocateTeamName,
@@ -882,14 +882,28 @@ function sumOptional(a, b) {
882
882
  if (a == null && b == null) return void 0;
883
883
  return (a ?? 0) + (b ?? 0);
884
884
  }
885
+ function requestOccupancy(u) {
886
+ return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
887
+ }
885
888
  function mergeUsage(a, b) {
886
889
  return {
887
890
  inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
888
891
  outputTokens: (a?.outputTokens ?? 0) + b.outputTokens,
889
892
  cacheReadTokens: sumOptional(a?.cacheReadTokens, b.cacheReadTokens),
890
- cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens)
893
+ cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens),
894
+ lastRequestTokens: b.lastRequestTokens ?? a?.lastRequestTokens
891
895
  };
892
896
  }
897
+ function applyTurnUsage(current, incoming, scope = "request") {
898
+ if (scope === "turn") {
899
+ return {
900
+ ...incoming,
901
+ lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming)
902
+ };
903
+ }
904
+ const merged = mergeUsage(current, incoming);
905
+ return { ...merged, lastRequestTokens: requestOccupancy(incoming) };
906
+ }
893
907
 
894
908
  // src/agents/spawn.ts
895
909
  async function spawnAgentTurn(thread, input, onEvent) {
@@ -899,9 +913,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
899
913
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
900
914
  );
901
915
  }
902
- const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-VF56FTPY.js");
916
+ const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-PJU6HISJ.js");
903
917
  if (isGlobalThread2(thread)) {
904
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-ZHBDHMZB.js");
918
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-RTUCCPCI.js");
905
919
  ensureGlobalCoordinatorCwd2(
906
920
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
907
921
  );
@@ -963,7 +977,7 @@ async function spawnAgentTurn(thread, input, onEvent) {
963
977
  continue;
964
978
  }
965
979
  if (parsed.type === "usage") {
966
- usage = mergeUsage(usage, parsed.data);
980
+ usage = applyTurnUsage(usage, parsed.data, parsed.scope ?? "request");
967
981
  onEvent(parsed);
968
982
  continue;
969
983
  }
@@ -1003,6 +1017,31 @@ async function spawnAgentTurn(thread, input, onEvent) {
1003
1017
  };
1004
1018
  }
1005
1019
 
1020
+ // src/git/agent-git-actions.ts
1021
+ var AGENT_GIT_ACTIONS = [
1022
+ "commit-push",
1023
+ "create-draft",
1024
+ "create-web",
1025
+ "resolve-conflicts",
1026
+ "merge"
1027
+ ];
1028
+ function agentGitPrompt(action, opts) {
1029
+ switch (action) {
1030
+ case "commit-push":
1031
+ return "Commit and push.";
1032
+ case "create-draft":
1033
+ return "Commit, push, and open a draft PR.";
1034
+ case "create-web":
1035
+ return "Commit, push, and open a PR in the browser.";
1036
+ case "resolve-conflicts": {
1037
+ const base = opts?.prBase?.trim().replace(/^refs\/heads\//, "");
1038
+ return base ? `Merge origin/${base} into this branch. Then push.` : "Fix merge conflicts.";
1039
+ }
1040
+ case "merge":
1041
+ return "Merge PR.";
1042
+ }
1043
+ }
1044
+
1006
1045
  // src/git/pr-merge-archive.ts
1007
1046
  function normalizePrState(state) {
1008
1047
  return (state ?? "").trim().toUpperCase();
@@ -1717,7 +1756,7 @@ async function createThread(input, _onSetupLine) {
1717
1756
  return readThread(thread.id) ?? thread;
1718
1757
  }
1719
1758
  async function listLinearIssues(agent, repoPath) {
1720
- const { getAdapter: getAdapter2 } = await import("../agents-77GE7VRW.js");
1759
+ const { getAdapter: getAdapter2 } = await import("../agents-2S2JVLZ6.js");
1721
1760
  await requireAgent(agent, { requireLinear: true });
1722
1761
  const adapter = getAdapter2(agent);
1723
1762
  if (!adapter.listLinearIssues) {
@@ -2581,7 +2620,7 @@ async function adoptThread(input) {
2581
2620
  messages: input.messages ?? []
2582
2621
  });
2583
2622
  writeThread(thread);
2584
- const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-ZJ45O4CD.js");
2623
+ const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-L4TXMUNM.js");
2585
2624
  await ensureWorkspace2(repoPath);
2586
2625
  return thread;
2587
2626
  }
@@ -3582,10 +3621,10 @@ async function getDiff(worktreePath, repoPath, opts) {
3582
3621
  listBranchCommits(worktreePath, repoPath, { base: baseLabel }),
3583
3622
  isDirty(worktreePath),
3584
3623
  countUnpushedCommits(worktreePath)
3585
- ]).then(([scopeStats, commits, dirty, unpushed]) => ({
3624
+ ]).then(([scopeStats, commits, dirty2, unpushed]) => ({
3586
3625
  scopeStats,
3587
3626
  commits,
3588
- dirty,
3627
+ dirty: dirty2,
3589
3628
  unpushed
3590
3629
  })) : Promise.resolve({
3591
3630
  scopeStats: emptyScopeStats(),
@@ -3662,13 +3701,14 @@ async function getDiff(worktreePath, repoPath, opts) {
3662
3701
  }
3663
3702
  }
3664
3703
  const files = [...filesMap.values()].sort((a, b) => a.path.localeCompare(b.path));
3704
+ const dirty = includeMeta ? meta.dirty : scope === "commits" ? false : files.length > 0;
3665
3705
  return {
3666
3706
  scope,
3667
3707
  commitSha: scope === "commits" ? commitSha : null,
3668
3708
  base: labelBase,
3669
3709
  files,
3670
3710
  stat: formatStat(files),
3671
- dirty: fileOnly || !includeMeta ? files.length > 0 : meta.dirty,
3711
+ dirty,
3672
3712
  unpushed: meta.unpushed,
3673
3713
  hasLastTurnBase,
3674
3714
  commits: meta.commits,
@@ -4380,7 +4420,7 @@ function formatRenameBranchDirective(thread, opts) {
4380
4420
  "- Rename the git branch to a short kebab-case name that describes this task (what you are changing), e.g. `fix/panel-width` or `feat/dark-mode`:",
4381
4421
  " `git branch -m <new-name>`",
4382
4422
  "- Prefer Conventional Commits style prefixes when they fit (`fix/`, `feat/`, `chore/`, `docs/`).",
4383
- "- Never push or merge to main/master from here."
4423
+ "- Never push this placeholder to main/master."
4384
4424
  ];
4385
4425
  const custom = opts?.customPrompt?.trim();
4386
4426
  if (custom) {
@@ -4423,7 +4463,7 @@ function formatWorktreeDirective(thread, opts) {
4423
4463
  "- Prefer a concise imperative title (Conventional Commits style when it fits: feat:/fix:/chore:/docs:). Body should summarize intent, key changes, and test notes."
4424
4464
  );
4425
4465
  lines.push(
4426
- "- Commit with messages that state the purpose of the change (same standard as the PR). Stay on this thread branch; never push or merge to main/master from here."
4466
+ "- Commit with messages that state the purpose of the change (same standard as the PR). Stay on this thread branch. Never push directly to main/master or merge locally into the main checkout. When asked to merge the PR, use GitHub from this worktree (`gh pr merge` / `gh stack merge`)."
4427
4467
  );
4428
4468
  if (thread.prUrl) {
4429
4469
  lines.push(
@@ -4455,13 +4495,13 @@ function formatWorktreeDirective(thread, opts) {
4455
4495
  '- "Fix CI: <name>." \u2192 investigate that failing check, fix it, commit, and push.'
4456
4496
  );
4457
4497
  lines.push(
4458
- '- "Update the branch." / "Fix merge conflicts." \u2192 sync with the PR base (merge or rebase), resolve conflicts carefully, commit, and push until the PR is mergeable.'
4498
+ '- "Update the branch." / "Fix merge conflicts." / "Merge origin/<base> into this branch. Then push." \u2192 sync with the PR base (merge or rebase), resolve conflicts carefully, commit, and push until the PR is mergeable.'
4459
4499
  );
4460
4500
  lines.push(
4461
4501
  '- "Address review comments." \u2192 read PR review feedback, make the requested changes, commit, and push.'
4462
4502
  );
4463
4503
  lines.push(
4464
- '- "Merge PR." \u2192 merge this thread\'s open pull request with `gh pr merge` (respect repo defaults / squash vs merge); do not force-push main/master.'
4504
+ '- "Merge PR." \u2192 merge this thread\'s open pull request on GitHub. If `gh stack view` shows a stack, use `gh stack merge`; otherwise `gh pr merge` (respect repo defaults / squash vs merge). Do not force-push main/master or merge locally into the main checkout.'
4465
4505
  );
4466
4506
  return lines.join("\n");
4467
4507
  }
@@ -5890,6 +5930,36 @@ var Orchestrator = class {
5890
5930
  this.emit({ type: "status_changed", threadId: tab.id, status: tab.status });
5891
5931
  return tab;
5892
5932
  }
5933
+ /**
5934
+ * Queue a desktop-git-button prompt on a worktree agent (commit/push/PR/merge).
5935
+ * Orchestrators use this instead of running git/gh from the synthetic home.
5936
+ */
5937
+ async askGit(threadRef, action) {
5938
+ if (!AGENT_GIT_ACTIONS.includes(action)) {
5939
+ throw new Error(`Unknown git action: ${action}`);
5940
+ }
5941
+ const thread = this.requireThread(threadRef);
5942
+ this.assertNotGlobal(thread, "ask_git");
5943
+ if (isOrchestratorThread(thread)) {
5944
+ throw new Error(
5945
+ "ask_git targets a worktree agent thread (not the orchestrator). Pass a child/worktree thread ref."
5946
+ );
5947
+ }
5948
+ if (action === "merge" && !thread.prUrl) {
5949
+ throw new Error(
5950
+ "No pull request linked. Ask the worktree agent to open a draft PR first (ask_git create-draft)."
5951
+ );
5952
+ }
5953
+ let prBase;
5954
+ if (action === "resolve-conflicts") {
5955
+ try {
5956
+ const details = await this.getPrDetails(threadRef);
5957
+ prBase = details?.baseRefName?.trim() || void 0;
5958
+ } catch {
5959
+ }
5960
+ }
5961
+ return this.send(threadRef, agentGitPrompt(action, { prBase }));
5962
+ }
5893
5963
  setThreadOptions(threadRef, patch) {
5894
5964
  const thread = this.requireThread(threadRef);
5895
5965
  const next = {};
@@ -5984,7 +6054,7 @@ var Orchestrator = class {
5984
6054
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
5985
6055
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
5986
6056
  try {
5987
- const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-ZJ45O4CD.js");
6057
+ const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-L4TXMUNM.js");
5988
6058
  await ensureWorkspace2(thread.repoPath);
5989
6059
  } catch {
5990
6060
  }
@@ -7206,7 +7276,7 @@ async function startMcpServer() {
7206
7276
  );
7207
7277
  server.tool(
7208
7278
  "send_to_thread",
7209
- "Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.",
7279
+ "Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. For commit/push/PR/merge, prefer ask_git (canonical desktop-button phrases). Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.",
7210
7280
  {
7211
7281
  ref: z2.string(),
7212
7282
  prompt: z2.string(),
@@ -7237,7 +7307,7 @@ async function startMcpServer() {
7237
7307
  );
7238
7308
  server.tool(
7239
7309
  "wait_for_turn",
7240
- "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread to read the agent reply.",
7310
+ "Block until the thread finishes its current/queued turn (avoids polling). Use after send_to_thread or ask_git to read the agent reply.",
7241
7311
  {
7242
7312
  ref: z2.string(),
7243
7313
  timeoutMs: z2.number().optional()
@@ -7302,7 +7372,7 @@ async function startMcpServer() {
7302
7372
  );
7303
7373
  server.tool(
7304
7374
  "archive_thread",
7305
- "Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators open PRs only by asking the worktree agent.",
7375
+ "Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, open PRs, and merge only by asking the worktree agent (ask_git).",
7306
7376
  { ref: z2.string() },
7307
7377
  async ({ ref }) => {
7308
7378
  const t = orch.getThread(ref);
@@ -7408,6 +7478,38 @@ async function startMcpServer() {
7408
7478
  }
7409
7479
  }
7410
7480
  );
7481
+ server.tool(
7482
+ "ask_git",
7483
+ "Tell a worktree agent to commit & push, open a draft PR, resolve conflicts, or merge the linked PR \u2014 same short prompts as the desktop git buttons. The worktree agent runs git/gh (`gh pr merge`); this only queues the prompt. Pass a worktree thread ref (not the orchestrator). Then wait_for_turn / get_turn_result. Do not run git or gh from the orchestration cwd.",
7484
+ {
7485
+ ref: z2.string().describe("Worktree thread id/ref"),
7486
+ action: z2.enum(AGENT_GIT_ACTIONS).describe(
7487
+ "commit-push | create-draft | create-web | resolve-conflicts | merge"
7488
+ )
7489
+ },
7490
+ async ({ ref, action }) => {
7491
+ try {
7492
+ const thread = await orch.askGit(ref, action);
7493
+ return {
7494
+ content: [
7495
+ {
7496
+ type: "text",
7497
+ text: JSON.stringify({
7498
+ id: thread.id,
7499
+ status: thread.status,
7500
+ queueLength: thread.queue.length,
7501
+ action,
7502
+ link: `sideboard://thread/${thread.id}`
7503
+ })
7504
+ }
7505
+ ]
7506
+ };
7507
+ } catch (err) {
7508
+ const message = err instanceof Error ? err.message : String(err);
7509
+ return { content: [{ type: "text", text: message }], isError: true };
7510
+ }
7511
+ }
7512
+ );
7411
7513
  const agentEnum = z2.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
7412
7514
  server.tool(
7413
7515
  "list_models",
@@ -7679,7 +7781,7 @@ async function startMcpServer() {
7679
7781
  );
7680
7782
  server.tool(
7681
7783
  "get_pr_stack",
7682
- "Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before mergePr on stacked PRs.",
7784
+ "Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs.",
7683
7785
  { ref: z2.string() },
7684
7786
  async ({ ref }) => {
7685
7787
  const stack = await orch.getPrStack(ref);
@@ -6,9 +6,9 @@ import {
6
6
  listWorkspaces,
7
7
  removeWorkspace,
8
8
  syncWorkspacesFromThreads
9
- } from "./chunk-CXT2PLO7.js";
10
- import "./chunk-POW7JCB5.js";
11
- import "./chunk-6RFPKZNC.js";
9
+ } from "./chunk-GJ2LZQJI.js";
10
+ import "./chunk-2NKSHIRI.js";
11
+ import "./chunk-XA2FQJTN.js";
12
12
  import "./chunk-OE7YBB5X.js";
13
13
  import "./chunk-B3SJXYIJ.js";
14
14
  import "./chunk-7FD7COKE.js";
@@ -4,9 +4,9 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-CYKPUTXN.js";
8
- import "./chunk-NF6Y4GTE.js";
9
- import "./chunk-V4ACEJX2.js";
7
+ } from "./chunk-DG3S2UXP.js";
8
+ import "./chunk-77W62MTX.js";
9
+ import "./chunk-HCWAIEBU.js";
10
10
  import "./chunk-W7YOTDVO.js";
11
11
  import "./chunk-JT7R45JB.js";
12
12
  import "./chunk-MF2YMEGD.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.73",
3
+ "version": "0.1.74",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",