pi-messenger 0.13.2 → 0.14.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/CHANGELOG.md +9 -0
- package/README.md +3 -1
- package/crew/index.ts +3 -0
- package/handlers.ts +113 -0
- package/index.ts +25 -28
- package/lib.ts +2 -1
- package/package.json +1 -1
- package/skills/pi-messenger-crew/SKILL.md +2 -1
- package/store.ts +14 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.14.0] - 2026-04-03
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **`leave` action** — `pi_messenger({ action: "leave" })` now lets the current session leave the mesh without restarting pi. It releases reservations, auto-unclaims the session's active swarm claim, closes the messenger overlay, and allows later rejoin from the same session.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Leave guardrails for Crew state** — `leave` now refuses while project planning, autonomous work, or session-owned in-progress Crew tasks are still active, preventing stranded coordination state.
|
|
12
|
+
- **Immutable-session cwd handling** — Registration, folder scoping, auto-register path matching, and messenger context now follow the live session cwd after pi runtime/session replacement instead of relying on `process.cwd()`.
|
|
13
|
+
|
|
5
14
|
## [0.13.2] - 2026-03-19
|
|
6
15
|
|
|
7
16
|
### Added
|
package/README.md
CHANGED
|
@@ -47,6 +47,7 @@ pi_messenger({ action: "join" })
|
|
|
47
47
|
pi_messenger({ action: "reserve", paths: ["src/auth/"], reason: "Refactoring" })
|
|
48
48
|
pi_messenger({ action: "send", to: "GoldFalcon", message: "auth is done" })
|
|
49
49
|
pi_messenger({ action: "release" })
|
|
50
|
+
pi_messenger({ action: "leave" })
|
|
50
51
|
```
|
|
51
52
|
|
|
52
53
|
For multi-agent task orchestration from a PRD:
|
|
@@ -67,7 +68,7 @@ pi_messenger({ action: "review", target: "task-1" }) // Reviewer checks imple
|
|
|
67
68
|
|
|
68
69
|
**Messaging** - Send messages between agents. Recipients wake up immediately and see the message as a steering prompt.
|
|
69
70
|
|
|
70
|
-
**File Reservations** - Claim files or directories. Other agents get blocked with a clear message telling them who to coordinate with. Auto-releases on exit.
|
|
71
|
+
**File Reservations** - Claim files or directories. Other agents get blocked with a clear message telling them who to coordinate with. Auto-releases on `leave` or exit.
|
|
71
72
|
|
|
72
73
|
**Stuck Detection** - Agents idle too long with an open task or reservation are flagged as stuck. Peers get a notification.
|
|
73
74
|
|
|
@@ -251,6 +252,7 @@ Agent definitions live in `crew/agents/` within the extension. To customize one
|
|
|
251
252
|
| Action | Description |
|
|
252
253
|
|--------|-------------|
|
|
253
254
|
| `join` | Join the agent mesh |
|
|
255
|
+
| `leave` | Leave the mesh for the current session |
|
|
254
256
|
| `list` | List agents with presence info |
|
|
255
257
|
| `status` | Show your status or crew progress |
|
|
256
258
|
| `whois` | Detailed info about an agent (`name` required) |
|
package/crew/index.ts
CHANGED
|
@@ -77,6 +77,9 @@ export async function executeCrewAction(
|
|
|
77
77
|
case 'status':
|
|
78
78
|
return handlers.executeStatus(state, dirs, ctx.cwd ?? process.cwd());
|
|
79
79
|
|
|
80
|
+
case 'leave':
|
|
81
|
+
return handlers.executeLeave(state, dirs, ctx);
|
|
82
|
+
|
|
80
83
|
case 'list':
|
|
81
84
|
return handlers.executeList(state, dirs, ctx.cwd ?? process.cwd(), { stuckThreshold: config?.stuckThreshold });
|
|
82
85
|
|
package/handlers.ts
CHANGED
|
@@ -26,6 +26,7 @@ import * as store from "./store.js";
|
|
|
26
26
|
import * as crewStore from "./crew/store.js";
|
|
27
27
|
import { getAutoRegisterPaths, saveAutoRegisterPaths, matchesAutoRegisterPath } from "./config.js";
|
|
28
28
|
import { readFeedEvents, logFeedEvent, pruneFeed, formatFeedLine, isCrewEvent, type FeedEvent } from "./feed.js";
|
|
29
|
+
import { isAutonomousForCwd, isPlanningForCwd } from "./crew/state.js";
|
|
29
30
|
import { loadCrewConfig } from "./crew/utils/config.js";
|
|
30
31
|
|
|
31
32
|
let messagesSentThisSession = 0;
|
|
@@ -127,6 +128,118 @@ export function executeJoin(
|
|
|
127
128
|
});
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
export async function executeLeave(
|
|
132
|
+
state: MessengerState,
|
|
133
|
+
dirs: Dirs,
|
|
134
|
+
ctx: ExtensionContext,
|
|
135
|
+
) {
|
|
136
|
+
if (!state.registered) {
|
|
137
|
+
return notRegisteredError();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
141
|
+
|
|
142
|
+
if (isPlanningForCwd(cwd)) {
|
|
143
|
+
return result(
|
|
144
|
+
"Cannot leave while Crew planning is active for this project. Cancel it first with pi_messenger({ action: \"plan.cancel\" }).",
|
|
145
|
+
{ mode: "leave", error: "planning_active" }
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (isAutonomousForCwd(cwd)) {
|
|
150
|
+
return result(
|
|
151
|
+
"Cannot leave while autonomous Crew work is active for this project. Stop it first with pi_messenger({ action: \"work.stop\" }).",
|
|
152
|
+
{ mode: "leave", error: "autonomous_active" }
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const inProgressTasks = crewStore
|
|
157
|
+
.getTasks(cwd)
|
|
158
|
+
.filter(task => task.status === "in_progress" && task.assigned_to === state.agentName)
|
|
159
|
+
.map(task => task.id);
|
|
160
|
+
|
|
161
|
+
if (inProgressTasks.length > 0) {
|
|
162
|
+
return result(
|
|
163
|
+
`Cannot leave while Crew task${inProgressTasks.length === 1 ? "" : "s"} assigned to you ${inProgressTasks.length === 1 ? "is" : "are"} still in progress: ${inProgressTasks.join(", ")}. Finish, block, or reset ${inProgressTasks.length === 1 ? "it" : "them"} first.`,
|
|
164
|
+
{ mode: "leave", error: "crew_tasks_in_progress", taskIds: inProgressTasks }
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const activeClaim = store.getAgentCurrentClaim(dirs, state.agentName);
|
|
169
|
+
let releasedClaim: { spec: string; taskId: string; reason?: string } | undefined;
|
|
170
|
+
if (activeClaim) {
|
|
171
|
+
const claimDisplay = displaySpecPath(activeClaim.spec, cwd);
|
|
172
|
+
try {
|
|
173
|
+
const unclaimResult = await store.unclaimTask(dirs, activeClaim.spec, activeClaim.taskId, state.agentName);
|
|
174
|
+
if (!store.isUnclaimSuccess(unclaimResult)) {
|
|
175
|
+
return result(
|
|
176
|
+
`Cannot leave because the active swarm claim ${activeClaim.taskId} in ${claimDisplay} could not be released. Resolve it first and retry.`,
|
|
177
|
+
{
|
|
178
|
+
mode: "leave",
|
|
179
|
+
error: unclaimResult.error,
|
|
180
|
+
activeClaim: { ...activeClaim, spec: claimDisplay },
|
|
181
|
+
...(store.isUnclaimNotYours(unclaimResult) ? { claimedBy: unclaimResult.claimedBy } : {}),
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
releasedClaim = activeClaim;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
188
|
+
return result(
|
|
189
|
+
`Cannot leave because the active swarm claim ${activeClaim.taskId} in ${claimDisplay} could not be released: ${message}`,
|
|
190
|
+
{
|
|
191
|
+
mode: "leave",
|
|
192
|
+
error: "unclaim_failed",
|
|
193
|
+
message,
|
|
194
|
+
activeClaim: { ...activeClaim, spec: claimDisplay },
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const releasedReservations = state.reservations.map(r => r.pattern);
|
|
201
|
+
state.reservations = [];
|
|
202
|
+
store.updateRegistration(state, dirs, ctx);
|
|
203
|
+
for (const pattern of releasedReservations) {
|
|
204
|
+
logFeedEvent(cwd, state.agentName, "release", pattern);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
store.unregister(state, dirs);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
211
|
+
return result(
|
|
212
|
+
`Could not leave pi-messenger: ${message}`,
|
|
213
|
+
{ mode: "leave", error: "unregister_failed", message }
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
logFeedEvent(cwd, state.agentName, "leave");
|
|
218
|
+
store.stopWatcher(state);
|
|
219
|
+
|
|
220
|
+
if (ctx.hasUI) {
|
|
221
|
+
ctx.ui.setStatus("messenger", undefined);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const claimText = releasedClaim
|
|
225
|
+
? `\nReleased claim: ${releasedClaim.taskId} in ${displaySpecPath(releasedClaim.spec, cwd)}`
|
|
226
|
+
: "";
|
|
227
|
+
|
|
228
|
+
return result(
|
|
229
|
+
`Left pi-messenger.${releasedReservations.length > 0 ? `\nReleased reservations: ${releasedReservations.join(", ")}` : ""}${claimText}`,
|
|
230
|
+
{
|
|
231
|
+
mode: "leave",
|
|
232
|
+
releasedReservations,
|
|
233
|
+
releasedClaim: releasedClaim
|
|
234
|
+
? {
|
|
235
|
+
...releasedClaim,
|
|
236
|
+
spec: displaySpecPath(releasedClaim.spec, cwd),
|
|
237
|
+
}
|
|
238
|
+
: undefined,
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
130
243
|
export function executeStatus(state: MessengerState, dirs: Dirs, cwd: string = process.cwd()) {
|
|
131
244
|
if (!state.registered) {
|
|
132
245
|
return notRegisteredError();
|
package/index.ts
CHANGED
|
@@ -80,7 +80,7 @@ export default function piMessengerExtension(pi: ExtensionAPI) {
|
|
|
80
80
|
// State & Configuration
|
|
81
81
|
// ===========================================================================
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
let config: MessengerConfig = loadConfig(process.cwd());
|
|
84
84
|
|
|
85
85
|
const state: MessengerState = {
|
|
86
86
|
agentName: process.env.PI_AGENT_NAME || "",
|
|
@@ -95,6 +95,7 @@ export default function piMessengerExtension(pi: ExtensionAPI) {
|
|
|
95
95
|
broadcastHistory: [],
|
|
96
96
|
seenSenders: new Map(),
|
|
97
97
|
model: "",
|
|
98
|
+
cwd: process.cwd(),
|
|
98
99
|
gitBranch: undefined,
|
|
99
100
|
spec: undefined,
|
|
100
101
|
scopeToFolder: config.scopeToFolder,
|
|
@@ -337,7 +338,7 @@ export default function piMessengerExtension(pi: ExtensionAPI) {
|
|
|
337
338
|
// ===========================================================================
|
|
338
339
|
|
|
339
340
|
function sendRegistrationContext(ctx: ExtensionContext): void {
|
|
340
|
-
const folder = extractFolder(
|
|
341
|
+
const folder = extractFolder(ctx.cwd ?? state.cwd);
|
|
341
342
|
const locationPart = state.gitBranch
|
|
342
343
|
? `${folder} on ${state.gitBranch}`
|
|
343
344
|
: folder;
|
|
@@ -360,6 +361,7 @@ export default function piMessengerExtension(pi: ExtensionAPI) {
|
|
|
360
361
|
Usage (action-based API - preferred):
|
|
361
362
|
// Coordination
|
|
362
363
|
pi_messenger({ action: "join" }) → Join mesh
|
|
364
|
+
pi_messenger({ action: "leave" }) → Leave mesh for this session
|
|
363
365
|
pi_messenger({ action: "status" }) → Get status
|
|
364
366
|
pi_messenger({ action: "list" }) → List agents with presence
|
|
365
367
|
pi_messenger({ action: "feed", limit: 20 }) → Activity feed
|
|
@@ -468,6 +470,13 @@ Usage (action-based API - preferred):
|
|
|
468
470
|
sendRegistrationContext(ctx);
|
|
469
471
|
}
|
|
470
472
|
|
|
473
|
+
if (action === "leave" && !state.registered) {
|
|
474
|
+
overlayHandle?.hide();
|
|
475
|
+
overlayHandle = null;
|
|
476
|
+
overlayTui = null;
|
|
477
|
+
overlayOpening = false;
|
|
478
|
+
}
|
|
479
|
+
|
|
471
480
|
return result;
|
|
472
481
|
}
|
|
473
482
|
});
|
|
@@ -769,6 +778,11 @@ Usage (action-based API - preferred):
|
|
|
769
778
|
|
|
770
779
|
pi.on("session_start", async (_event, ctx) => {
|
|
771
780
|
latestCtx = ctx;
|
|
781
|
+
state.cwd = ctx.cwd ?? process.cwd();
|
|
782
|
+
config = loadConfig(state.cwd);
|
|
783
|
+
state.scopeToFolder = config.scopeToFolder;
|
|
784
|
+
nameTheme.theme = config.nameTheme;
|
|
785
|
+
nameTheme.customWords = config.nameWords;
|
|
772
786
|
resetAutonomousContinueGuard();
|
|
773
787
|
startStatusHeartbeat();
|
|
774
788
|
for (const entry of ctx.sessionManager.getEntries()) {
|
|
@@ -776,7 +790,7 @@ Usage (action-based API - preferred):
|
|
|
776
790
|
restoreAutonomousState(entry.data as Parameters<typeof restoreAutonomousState>[0]);
|
|
777
791
|
}
|
|
778
792
|
}
|
|
779
|
-
const { staleCleared } = restorePlanningState(
|
|
793
|
+
const { staleCleared } = restorePlanningState(state.cwd);
|
|
780
794
|
if (staleCleared && ctx.hasUI) {
|
|
781
795
|
ctx.ui.notify("Stale planning state cleared (planner process exited)", "warning");
|
|
782
796
|
}
|
|
@@ -785,7 +799,7 @@ Usage (action-based API - preferred):
|
|
|
785
799
|
try { fs.rmSync(join(homedir(), ".pi/agent/messenger/feed.jsonl"), { force: true }); } catch {}
|
|
786
800
|
|
|
787
801
|
const shouldAutoRegister = config.autoRegister ||
|
|
788
|
-
matchesAutoRegisterPath(
|
|
802
|
+
matchesAutoRegisterPath(state.cwd, config.autoRegisterPaths);
|
|
789
803
|
|
|
790
804
|
if (!shouldAutoRegister) {
|
|
791
805
|
maybeAutoOpenCrewOverlay(ctx);
|
|
@@ -793,7 +807,7 @@ Usage (action-based API - preferred):
|
|
|
793
807
|
}
|
|
794
808
|
|
|
795
809
|
if (store.register(state, dirs, ctx, nameTheme)) {
|
|
796
|
-
const cwd =
|
|
810
|
+
const cwd = state.cwd;
|
|
797
811
|
store.startWatcher(state, dirs, deliverMessage);
|
|
798
812
|
updateStatus(ctx);
|
|
799
813
|
pruneFeed(cwd, config.feedRetention);
|
|
@@ -889,28 +903,6 @@ Usage (action-based API - preferred):
|
|
|
889
903
|
});
|
|
890
904
|
}
|
|
891
905
|
|
|
892
|
-
pi.on("session_switch", async (_event, ctx) => {
|
|
893
|
-
latestCtx = ctx;
|
|
894
|
-
resetAutonomousContinueGuard();
|
|
895
|
-
const { staleCleared } = restorePlanningState(ctx.cwd ?? process.cwd());
|
|
896
|
-
if (staleCleared && ctx.hasUI) {
|
|
897
|
-
ctx.ui.notify("Stale planning state cleared (planner process exited)", "warning");
|
|
898
|
-
}
|
|
899
|
-
recoverWatcherIfNeeded();
|
|
900
|
-
updateStatus(ctx);
|
|
901
|
-
maybeAutoOpenCrewOverlay(ctx);
|
|
902
|
-
});
|
|
903
|
-
pi.on("session_fork", async (_event, ctx) => {
|
|
904
|
-
latestCtx = ctx;
|
|
905
|
-
resetAutonomousContinueGuard();
|
|
906
|
-
const { staleCleared } = restorePlanningState(ctx.cwd ?? process.cwd());
|
|
907
|
-
if (staleCleared && ctx.hasUI) {
|
|
908
|
-
ctx.ui.notify("Stale planning state cleared (planner process exited)", "warning");
|
|
909
|
-
}
|
|
910
|
-
recoverWatcherIfNeeded();
|
|
911
|
-
updateStatus(ctx);
|
|
912
|
-
maybeAutoOpenCrewOverlay(ctx);
|
|
913
|
-
});
|
|
914
906
|
pi.on("session_tree", async (_event, ctx) => {
|
|
915
907
|
latestCtx = ctx;
|
|
916
908
|
const { staleCleared } = restorePlanningState(ctx.cwd ?? process.cwd());
|
|
@@ -1101,7 +1093,12 @@ Usage (action-based API - preferred):
|
|
|
1101
1093
|
if (recentTestTimer) { clearTimeout(recentTestTimer); recentTestTimer = null; }
|
|
1102
1094
|
if (recentEditTimer) { clearTimeout(recentEditTimer); recentEditTimer = null; }
|
|
1103
1095
|
store.stopWatcher(state);
|
|
1104
|
-
|
|
1096
|
+
try {
|
|
1097
|
+
store.unregister(state, dirs);
|
|
1098
|
+
} catch {
|
|
1099
|
+
// Safe to ignore during shutdown: the process is exiting, so any leftover
|
|
1100
|
+
// registration will be cleaned up as stale on the next registry read.
|
|
1101
|
+
}
|
|
1105
1102
|
});
|
|
1106
1103
|
|
|
1107
1104
|
// ===========================================================================
|
package/lib.ts
CHANGED
|
@@ -73,6 +73,7 @@ export interface MessengerState {
|
|
|
73
73
|
broadcastHistory: AgentMailMessage[];
|
|
74
74
|
seenSenders: Map<string, string>;
|
|
75
75
|
model: string;
|
|
76
|
+
cwd: string;
|
|
76
77
|
gitBranch?: string;
|
|
77
78
|
spec?: string;
|
|
78
79
|
scopeToFolder: boolean;
|
|
@@ -388,8 +389,8 @@ export function buildSelfRegistration(state: MessengerState): AgentRegistration
|
|
|
388
389
|
name: state.agentName,
|
|
389
390
|
pid: process.pid,
|
|
390
391
|
sessionId: "",
|
|
391
|
-
cwd: process.cwd(),
|
|
392
392
|
model: state.model,
|
|
393
|
+
cwd: state.cwd,
|
|
393
394
|
startedAt: state.sessionStartedAt,
|
|
394
395
|
gitBranch: state.gitBranch,
|
|
395
396
|
spec: state.spec,
|
package/package.json
CHANGED
|
@@ -9,9 +9,10 @@ Use pi-messenger for multi-agent coordination and Crew task orchestration.
|
|
|
9
9
|
|
|
10
10
|
## Quick Reference
|
|
11
11
|
|
|
12
|
-
### Join the Mesh
|
|
12
|
+
### Join or Leave the Mesh
|
|
13
13
|
```typescript
|
|
14
14
|
pi_messenger({ action: "join" })
|
|
15
|
+
pi_messenger({ action: "leave" })
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
### Check Status
|
package/store.ts
CHANGED
|
@@ -167,7 +167,7 @@ export function getRegistrationPath(state: MessengerState, dirs: Dirs): string {
|
|
|
167
167
|
export function getActiveAgents(state: MessengerState, dirs: Dirs): AgentRegistration[] {
|
|
168
168
|
const now = Date.now();
|
|
169
169
|
const excludeName = state.agentName;
|
|
170
|
-
const myCwd = normalizeCwd(
|
|
170
|
+
const myCwd = normalizeCwd(state.cwd);
|
|
171
171
|
const scopeToFolder = state.scopeToFolder;
|
|
172
172
|
|
|
173
173
|
// Cache key includes scopeToFolder and cwd for proper cache invalidation
|
|
@@ -339,7 +339,7 @@ export function register(state: MessengerState, dirs: Dirs, ctx: ExtensionContex
|
|
|
339
339
|
|
|
340
340
|
ensureDirSync(getMyInbox(state, dirs));
|
|
341
341
|
|
|
342
|
-
const cwd = normalizeCwd(process.cwd());
|
|
342
|
+
const cwd = normalizeCwd(ctx.cwd ?? process.cwd());
|
|
343
343
|
const gitBranch = getGitBranch(cwd);
|
|
344
344
|
const now = new Date().toISOString();
|
|
345
345
|
const registration: AgentRegistration = {
|
|
@@ -378,6 +378,7 @@ export function register(state: MessengerState, dirs: Dirs, ctx: ExtensionContex
|
|
|
378
378
|
if (verified) {
|
|
379
379
|
state.registered = true;
|
|
380
380
|
state.model = ctx.model?.id ?? "unknown";
|
|
381
|
+
state.cwd = cwd;
|
|
381
382
|
state.gitBranch = gitBranch;
|
|
382
383
|
state.activity.lastActivityAt = now;
|
|
383
384
|
invalidateAgentsCache();
|
|
@@ -426,6 +427,7 @@ export function updateRegistration(state: MessengerState, dirs: Dirs, ctx: Exten
|
|
|
426
427
|
const currentModel = ctx.model?.id ?? reg.model;
|
|
427
428
|
reg.model = currentModel;
|
|
428
429
|
state.model = currentModel;
|
|
430
|
+
reg.cwd = state.cwd;
|
|
429
431
|
reg.reservations = state.reservations.length > 0 ? state.reservations : undefined;
|
|
430
432
|
if (state.spec) {
|
|
431
433
|
reg.spec = state.spec;
|
|
@@ -452,6 +454,7 @@ export function flushActivityToRegistry(state: MessengerState, dirs: Dirs, ctx:
|
|
|
452
454
|
const currentModel = ctx.model?.id ?? reg.model;
|
|
453
455
|
reg.model = currentModel;
|
|
454
456
|
state.model = currentModel;
|
|
457
|
+
reg.cwd = state.cwd;
|
|
455
458
|
reg.session = { ...state.session };
|
|
456
459
|
reg.activity = { ...state.activity };
|
|
457
460
|
reg.statusMessage = state.statusMessage;
|
|
@@ -464,11 +467,15 @@ export function flushActivityToRegistry(state: MessengerState, dirs: Dirs, ctx:
|
|
|
464
467
|
export function unregister(state: MessengerState, dirs: Dirs): void {
|
|
465
468
|
if (!state.registered) return;
|
|
466
469
|
|
|
470
|
+
const regPath = getRegistrationPath(state, dirs);
|
|
467
471
|
try {
|
|
468
|
-
fs.unlinkSync(
|
|
469
|
-
} catch {
|
|
470
|
-
|
|
472
|
+
fs.unlinkSync(regPath);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (fs.existsSync(regPath)) {
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
471
477
|
}
|
|
478
|
+
|
|
472
479
|
state.registered = false;
|
|
473
480
|
invalidateAgentsCache();
|
|
474
481
|
}
|
|
@@ -515,7 +522,7 @@ export function renameAgent(
|
|
|
515
522
|
|
|
516
523
|
processAllPendingMessages(state, dirs, deliverFn);
|
|
517
524
|
|
|
518
|
-
const cwd = normalizeCwd(process.cwd());
|
|
525
|
+
const cwd = normalizeCwd(ctx.cwd ?? process.cwd());
|
|
519
526
|
const gitBranch = getGitBranch(cwd);
|
|
520
527
|
const now = new Date().toISOString();
|
|
521
528
|
const registration: AgentRegistration = {
|
|
@@ -598,6 +605,7 @@ export function renameAgent(
|
|
|
598
605
|
}
|
|
599
606
|
|
|
600
607
|
state.model = ctx.model?.id ?? "unknown";
|
|
608
|
+
state.cwd = cwd;
|
|
601
609
|
state.gitBranch = gitBranch;
|
|
602
610
|
state.sessionStartedAt = now;
|
|
603
611
|
state.activity.lastActivityAt = now;
|