opencode-pilot 0.24.9 → 0.24.10

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.
@@ -1,8 +1,8 @@
1
1
  class OpencodePilot < Formula
2
2
  desc "Automation daemon for OpenCode - polls GitHub/Linear issues and spawns sessions"
3
3
  homepage "https://github.com/athal7/opencode-pilot"
4
- url "https://github.com/athal7/opencode-pilot/archive/refs/tags/v0.24.8.tar.gz"
5
- sha256 "23b550fd74fee8f2fd8c60c8358f67065ee77af921fdc3bc2ff26a6cfef68ae6"
4
+ url "https://github.com/athal7/opencode-pilot/archive/refs/tags/v0.24.9.tar.gz"
5
+ sha256 "b477a5677fb80456db3f4f6464dd2071d0efb56bdd29b8a8fc01e5d56638f588"
6
6
  license "MIT"
7
7
 
8
8
  depends_on "node"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-pilot",
3
- "version": "0.24.9",
3
+ "version": "0.24.10",
4
4
  "type": "module",
5
5
  "main": "plugin/index.js",
6
6
  "description": "Automation daemon for OpenCode - polls for work and spawns sessions",
@@ -687,16 +687,17 @@ export async function findReusableSession(serverUrl, directory, options = {}) {
687
687
  export async function createSessionViaApi(serverUrl, directory, prompt, options = {}) {
688
688
  const fetchFn = options.fetch || fetch;
689
689
  const headerTimeout = options.headerTimeout || HEADER_TIMEOUT_MS;
690
- // Use project directory for session creation (determines projectID in OpenCode).
691
- // The working directory (which may be a worktree) is used for messages/commands.
692
690
  const projectDir = options.projectDirectory || directory;
693
691
 
694
692
  let session = null;
695
693
 
696
694
  try {
697
- // Step 1: Create session scoped to the project directory
695
+ // Step 1: Create session with the working directory (may be a worktree).
696
+ // This sets the session's working directory so the agent operates in the
697
+ // correct location. For worktree sessions, the server may assign
698
+ // projectID 'global' since sandbox paths don't match project worktrees.
698
699
  const sessionUrl = new URL('/session', serverUrl);
699
- sessionUrl.searchParams.set('directory', projectDir);
700
+ sessionUrl.searchParams.set('directory', directory);
700
701
 
701
702
  const createResponse = await fetchFn(sessionUrl.toString(), {
702
703
  method: 'POST',
@@ -712,14 +713,20 @@ export async function createSessionViaApi(serverUrl, directory, prompt, options
712
713
  session = await createResponse.json();
713
714
  debug(`createSessionViaApi: created session ${session.id} in ${directory}`);
714
715
 
715
- // Step 2: Update session title if provided
716
- if (options.title) {
716
+ // Step 2: Update session - always PATCH with project directory when it
717
+ // differs from the working directory (worktree case). This re-associates
718
+ // the session with the correct project so it appears in the UI.
719
+ // Also set the title if provided.
720
+ const needsProjectScoping = projectDir !== directory;
721
+ if (options.title || needsProjectScoping) {
717
722
  const updateUrl = new URL(`/session/${session.id}`, serverUrl);
718
723
  updateUrl.searchParams.set('directory', projectDir);
724
+ const patchBody = {};
725
+ if (options.title) patchBody.title = options.title;
719
726
  await fetchFn(updateUrl.toString(), {
720
727
  method: 'PATCH',
721
728
  headers: { 'Content-Type': 'application/json' },
722
- body: JSON.stringify({ title: options.title }),
729
+ body: JSON.stringify(patchBody),
723
730
  });
724
731
  }
725
732
 
@@ -564,9 +564,9 @@ describe("integration: worktree creation with worktree_name", () => {
564
564
  assert.ok(worktreeCreateCalled, "Should create worktree when worktree_name is configured");
565
565
  assert.strictEqual(createdWorktreeName, "pr-42", "Should expand worktree_name template");
566
566
  assert.ok(sessionCreated, "Should create session");
567
- // Session creation uses the project directory (for correct projectID scoping)
568
- // The worktree path is used for messages/commands (file operations)
569
- assert.strictEqual(sessionDirectory, "/proj", "Session should be scoped to project directory");
567
+ // Session creation uses the worktree directory (sets working directory)
568
+ // The PATCH with project directory re-associates it with the correct project
569
+ assert.strictEqual(sessionDirectory, "/worktree/pr-42", "Session should be created in worktree directory");
570
570
  });
571
571
 
572
572
  it("reuses stored directory when reprocessing same item", async () => {
@@ -618,8 +618,8 @@ describe("integration: worktree creation with worktree_name", () => {
618
618
  assert.ok(result.success, "Action should succeed");
619
619
  // Should NOT create a new worktree since we have existing_directory
620
620
  assert.strictEqual(worktreeCreateCalled, false, "Should NOT create new worktree when existing_directory provided");
621
- // Session creation uses the project directory (for correct projectID scoping)
622
- assert.strictEqual(sessionDirectory, "/proj", "Session should be scoped to project directory");
621
+ // Session creation uses the existing worktree directory (sets working directory)
622
+ assert.strictEqual(sessionDirectory, existingWorktreeDir, "Session should be created in existing worktree directory");
623
623
  });
624
624
 
625
625
  it("skips session reuse when working in a worktree", async () => {
@@ -631,6 +631,7 @@ describe("integration: worktree creation with worktree_name", () => {
631
631
  let sessionListQueried = false;
632
632
  let sessionCreated = false;
633
633
  let sessionCreateDirectory = null;
634
+ let patchDirectory = null;
634
635
 
635
636
  const existingWorktreeDir = "/worktree/calm-wizard";
636
637
 
@@ -654,7 +655,10 @@ describe("integration: worktree creation with worktree_name", () => {
654
655
  sessionCreateDirectory = req.query?.directory;
655
656
  return { body: { id: "ses_new" } };
656
657
  },
657
- "PATCH /session/ses_new": () => ({ body: {} }),
658
+ "PATCH /session/ses_new": (req) => {
659
+ patchDirectory = req.query?.directory;
660
+ return { body: {} };
661
+ },
658
662
  "POST /session/ses_new/message": () => ({ body: { success: true } }),
659
663
  "POST /session/ses_new/command": () => ({ body: { success: true } }),
660
664
  });
@@ -674,10 +678,13 @@ describe("integration: worktree creation with worktree_name", () => {
674
678
  // Should NOT query for existing sessions when in a worktree
675
679
  assert.strictEqual(sessionListQueried, false,
676
680
  "Should skip session reuse entirely when in a worktree");
677
- // Should create a new session scoped to the project directory
681
+ // Should create a new session with the worktree directory (correct working dir)
678
682
  assert.ok(sessionCreated, "Should create a new session");
679
- assert.strictEqual(sessionCreateDirectory, "/proj",
680
- "New session should be scoped to project directory");
683
+ assert.strictEqual(sessionCreateDirectory, existingWorktreeDir,
684
+ "Session should be created in worktree directory");
685
+ // PATCH re-associates the session with the correct project
686
+ assert.strictEqual(patchDirectory, "/proj",
687
+ "PATCH should use project directory for correct project scoping");
681
688
  });
682
689
  });
683
690
 
@@ -981,9 +981,9 @@ Check for bugs and security issues.`;
981
981
  // Should NOT call worktree endpoints when existing_directory is provided
982
982
  assert.strictEqual(worktreeListCalled, false, 'Should NOT list worktrees');
983
983
  assert.strictEqual(worktreeCreateCalled, false, 'Should NOT create worktree');
984
- // Session creation uses project directory (for correct projectID scoping)
985
- assert.strictEqual(sessionDirectory, '/data/proj',
986
- 'Session creation should use project directory, not worktree');
984
+ // Session creation uses the worktree directory (sets correct working directory)
985
+ assert.strictEqual(sessionDirectory, '/data/worktree/calm-wizard',
986
+ 'Session creation should use worktree directory for correct working dir');
987
987
  // Result directory is the worktree (where file operations happen)
988
988
  assert.strictEqual(result.directory, '/data/worktree/calm-wizard',
989
989
  'Result should include worktree directory');
@@ -1042,11 +1042,12 @@ Check for bugs and security issues.`;
1042
1042
  assert.ok(messageUrl.includes('%2Fpath%2Fto%2Fproject'), 'Message URL should include encoded directory path');
1043
1043
  });
1044
1044
 
1045
- test('uses projectDirectory for session creation, working directory for messages', async () => {
1045
+ test('creates session with worktree directory, patches with project directory for scoping', async () => {
1046
1046
  const { createSessionViaApi } = await import('../../service/actions.js');
1047
1047
 
1048
1048
  const mockSessionId = 'ses_test_proj';
1049
1049
  let createUrl = null;
1050
+ let patchUrl = null;
1050
1051
  let messageUrl = null;
1051
1052
 
1052
1053
  const mockFetch = async (url, opts) => {
@@ -1057,6 +1058,11 @@ Check for bugs and security issues.`;
1057
1058
  return { ok: true, json: async () => ({ id: mockSessionId }) };
1058
1059
  }
1059
1060
 
1061
+ if (urlObj.pathname.includes(`/session/${mockSessionId}`) && opts?.method === 'PATCH') {
1062
+ patchUrl = url;
1063
+ return { ok: true, json: async () => ({}) };
1064
+ }
1065
+
1060
1066
  if (urlObj.pathname.includes('/message') && opts?.method === 'POST') {
1061
1067
  messageUrl = url;
1062
1068
  return { ok: true, json: async () => ({ success: true }) };
@@ -1075,13 +1081,18 @@ Check for bugs and security issues.`;
1075
1081
  }
1076
1082
  );
1077
1083
 
1078
- // Session creation should use the project directory (for correct projectID scoping)
1079
- assert.ok(createUrl.includes('%2Fhome%2Fuser%2Fcode%2Fodin'),
1080
- 'Session creation should use projectDirectory');
1081
- assert.ok(!createUrl.includes('worktree'),
1082
- 'Session creation should NOT use the worktree path');
1084
+ // Session creation should use the worktree directory (sets working directory)
1085
+ assert.ok(createUrl.includes('worktree'),
1086
+ 'Session creation should use the worktree path');
1087
+ assert.ok(createUrl.includes('pr-415'),
1088
+ 'Session creation should use the worktree path');
1089
+
1090
+ // PATCH should use the project directory (re-associates with correct project)
1091
+ assert.ok(patchUrl, 'PATCH should be called for project scoping');
1092
+ assert.ok(patchUrl.includes('%2Fhome%2Fuser%2Fcode%2Fodin'),
1093
+ 'PATCH should use projectDirectory for project scoping');
1083
1094
 
1084
- // Message should use the working directory (for file operations in the worktree)
1095
+ // Message should use the working directory (worktree)
1085
1096
  assert.ok(messageUrl.includes('worktree'),
1086
1097
  'Message should use the worktree working directory');
1087
1098
  assert.ok(messageUrl.includes('pr-415'),