@obtoai/agent-bridge 0.1.0-beta.20 → 0.1.0-beta.21

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/package.json +1 -1
  2. package/src/daemon.js +43 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obtoai/agent-bridge",
3
- "version": "0.1.0-beta.20",
3
+ "version": "0.1.0-beta.21",
4
4
  "description": "Local consumer for the OBTO Agent Bridge. Receives bridge events over SSE and drives a coding agent (Claude Code or OpenAI Codex) on your machine.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "OBTO Inc.",
package/src/daemon.js CHANGED
@@ -114,29 +114,52 @@ const handleEvent = async (sseEvent) => {
114
114
  // session is persisted to state.json and externalAdoption stops mattering.
115
115
  if (!session && payload.externalAdoption && payload.externalAdoption.sessionId) {
116
116
  const ea = payload.externalAdoption;
117
- // Prefer projectName (already-decoded filesystem path) for cwd. Claude
118
- // scanner emits both raw `-Users-foo` and decoded `/Users/foo`; the
119
- // decoded form is what the SDK's cwd argument expects.
120
- let resumeCwd = ea.projectName || ea.projectDir || cfg.projectDir;
117
+ // Prefer projectName (already-decoded absolute path) for the SDK's cwd.
118
+ // Code-review (2026-06-06): the prior fallback to cfg.projectDir was a
119
+ // silent foot-gun if the external record carried no usable cwd (e.g.,
120
+ // a Codex session whose meta line didn't include `cwd`), we'd resume the
121
+ // session in the daemon's working dir, NOT the original project. The
122
+ // agent would find unrelated files and the conversation would lose its
123
+ // grounding. Now we validate the path looks absolute; if it doesn't, we
124
+ // log loudly and decline to fake a resume — the driver first-touches in
125
+ // cfg.projectDir, which is the only honest behavior when the original
126
+ // cwd is unknown.
127
+ let resumeCwd = ea.projectName || ea.projectDir || '';
121
128
  if (typeof resumeCwd === 'string' && resumeCwd.startsWith('//')) {
122
129
  resumeCwd = '/' + resumeCwd.replace(/^\/+/, '');
123
130
  }
124
- session = {
125
- sessionId: ea.sessionId,
126
- projectDir: resumeCwd,
127
- jsonlPath: null,
128
- lastJsonlMtimeMs: null,
129
- createdAt: new Date().toISOString(),
130
- lastDriveAt: null,
131
- };
132
- wasAdoption = true;
133
- log('info', 'adopting external session on first touch', {
134
- threadId,
135
- agent,
136
- sessionId: ea.sessionId,
137
- cwd: resumeCwd,
138
- source: ea.source,
139
- });
131
+ const looksAbsolute = typeof resumeCwd === 'string' && resumeCwd.startsWith('/');
132
+ if (!looksAbsolute) {
133
+ // The external adoption record didn't carry a real filesystem path —
134
+ // we will NOT synthesize a binding pointing at the daemon's working
135
+ // dir, since that would silently misroute the agent to unrelated
136
+ // files. Skip the synthetic binding; the driver will first-touch
137
+ // fresh, which is honest about losing the original context.
138
+ log('warn', 'external adoption skipped: no usable cwd in adoption record — first-touching fresh', {
139
+ threadId,
140
+ agent,
141
+ sessionId: ea.sessionId,
142
+ gotProjectName: ea.projectName,
143
+ gotProjectDir: ea.projectDir,
144
+ });
145
+ } else {
146
+ session = {
147
+ sessionId: ea.sessionId,
148
+ projectDir: resumeCwd,
149
+ jsonlPath: null,
150
+ lastJsonlMtimeMs: null,
151
+ createdAt: new Date().toISOString(),
152
+ lastDriveAt: null,
153
+ };
154
+ wasAdoption = true;
155
+ log('info', 'adopting external session on first touch', {
156
+ threadId,
157
+ agent,
158
+ sessionId: ea.sessionId,
159
+ cwd: resumeCwd,
160
+ source: ea.source,
161
+ });
162
+ }
140
163
  }
141
164
 
142
165
  log('event', 'reply received', {