@hienlh/ppm 0.2.13 → 0.2.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -13,6 +13,34 @@ import type {
13
13
  UsageInfo,
14
14
  } from "./provider.interface.ts";
15
15
  import { configService } from "../services/config.service.ts";
16
+ import { resolve } from "node:path";
17
+ import { homedir } from "node:os";
18
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
19
+
20
+ /** Persistent PPM sessionId → SDK sessionId mapping */
21
+ const SESSION_MAP_FILE = resolve(homedir(), ".ppm", "session-map.json");
22
+
23
+ function loadSessionMap(): Record<string, string> {
24
+ try {
25
+ if (existsSync(SESSION_MAP_FILE)) return JSON.parse(readFileSync(SESSION_MAP_FILE, "utf-8"));
26
+ } catch {}
27
+ return {};
28
+ }
29
+
30
+ function saveSessionMapping(ppmId: string, sdkId: string): void {
31
+ const map = loadSessionMap();
32
+ map[ppmId] = sdkId;
33
+ try {
34
+ const dir = resolve(homedir(), ".ppm");
35
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
36
+ writeFileSync(SESSION_MAP_FILE, JSON.stringify(map));
37
+ } catch {}
38
+ }
39
+
40
+ function getSdkSessionId(ppmId: string): string {
41
+ const map = loadSessionMap();
42
+ return map[ppmId] ?? ppmId;
43
+ }
16
44
 
17
45
  /**
18
46
  * Pending approval: canUseTool callback creates a promise,
@@ -225,13 +253,15 @@ export class ClaudeAgentSdkProvider implements AIProvider {
225
253
 
226
254
  try {
227
255
  const providerConfig = this.getProviderConfig();
228
- console.log(`[sdk] query: session=${sessionId} isFirst=${isFirstMessage} cwd=${meta.projectPath ?? "(none)"} resume=${!isFirstMessage ? sessionId : "N/A"}`);
256
+ // Resolve SDK's actual session ID for resume (may differ from PPM's UUID)
257
+ const sdkId = getSdkSessionId(sessionId);
258
+ console.log(`[sdk] query: session=${sessionId} sdkId=${sdkId} isFirst=${isFirstMessage} cwd=${meta.projectPath ?? "(none)"}`);
229
259
 
230
260
  const q = query({
231
261
  prompt: message,
232
262
  options: {
233
263
  sessionId: isFirstMessage ? sessionId : undefined,
234
- resume: isFirstMessage ? undefined : sessionId,
264
+ resume: isFirstMessage ? undefined : sdkId,
235
265
  cwd: meta.projectPath,
236
266
  // Use full Claude Code system prompt (coding guidelines, security, response style)
237
267
  systemPrompt: { type: "preset", preset: "claude_code" },
@@ -286,7 +316,9 @@ export class ClaudeAgentSdkProvider implements AIProvider {
286
316
  const initMsg = msg as any;
287
317
  // SDK may assign a different session_id than our UUID
288
318
  if (initMsg.session_id && initMsg.session_id !== sessionId) {
289
- // Update our mapping so resume works with SDK's actual ID
319
+ // Persist mapping so resume works after server restart
320
+ saveSessionMapping(sessionId, initMsg.session_id);
321
+ // Update our in-memory mapping
290
322
  const oldMeta = this.activeSessions.get(sessionId);
291
323
  if (oldMeta) {
292
324
  this.activeSessions.set(initMsg.session_id, { ...oldMeta, id: initMsg.session_id });
@@ -299,7 +331,7 @@ export class ClaudeAgentSdkProvider implements AIProvider {
299
331
  // the SDK has finished executing tools. Fetch tool_results from session history.
300
332
  if (pendingToolCount > 0 && (msg.type === "assistant" || (msg as any).type === "partial" || (msg as any).type === "stream_event")) {
301
333
  try {
302
- const sessionMsgs = await getSessionMessages(sessionId);
334
+ const sessionMsgs = await getSessionMessages(sdkId);
303
335
  // Find the last user message — it contains tool_result blocks
304
336
  const lastUserMsg = [...sessionMsgs].reverse().find(
305
337
  (m: any) => m.type === "user",
@@ -404,7 +436,7 @@ export class ClaudeAgentSdkProvider implements AIProvider {
404
436
  // Flush any remaining pending tool_results before finishing
405
437
  if (pendingToolCount > 0) {
406
438
  try {
407
- const sessionMsgs = await getSessionMessages(sessionId);
439
+ const sessionMsgs = await getSessionMessages(sdkId);
408
440
  const lastUserMsg = [...sessionMsgs].reverse().find(
409
441
  (m: any) => m.type === "user",
410
442
  );
@@ -500,7 +532,8 @@ export class ClaudeAgentSdkProvider implements AIProvider {
500
532
 
501
533
  async getMessages(sessionId: string): Promise<ChatMessage[]> {
502
534
  try {
503
- const messages = await getSessionMessages(sessionId);
535
+ const sdkId = getSdkSessionId(sessionId);
536
+ const messages = await getSessionMessages(sdkId);
504
537
  const parsed = messages.map((msg) => parseSessionMessage(msg));
505
538
 
506
539
  // Merge tool_result user messages into the preceding assistant message