@sumicom/quicksave 0.7.1 → 0.8.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.
Files changed (60) hide show
  1. package/dist/ai/claudeCliProvider.d.ts.map +1 -1
  2. package/dist/ai/claudeCliProvider.js +8 -11
  3. package/dist/ai/claudeCliProvider.js.map +1 -1
  4. package/dist/ai/claudeSdkProvider.d.ts.map +1 -1
  5. package/dist/ai/claudeSdkProvider.js +9 -14
  6. package/dist/ai/claudeSdkProvider.js.map +1 -1
  7. package/dist/ai/enrichEntry.d.ts +11 -0
  8. package/dist/ai/enrichEntry.d.ts.map +1 -0
  9. package/dist/ai/enrichEntry.js +27 -0
  10. package/dist/ai/enrichEntry.js.map +1 -0
  11. package/dist/ai/provider.d.ts +6 -0
  12. package/dist/ai/provider.d.ts.map +1 -1
  13. package/dist/ai/sandboxMcp.d.ts +41 -3
  14. package/dist/ai/sandboxMcp.d.ts.map +1 -1
  15. package/dist/ai/sandboxMcp.js +44 -3
  16. package/dist/ai/sandboxMcp.js.map +1 -1
  17. package/dist/ai/sandboxMcpStdio.js +103 -9
  18. package/dist/ai/sandboxMcpStdio.js.map +1 -1
  19. package/dist/ai/sessionManager.d.ts +14 -2
  20. package/dist/ai/sessionManager.d.ts.map +1 -1
  21. package/dist/ai/sessionManager.js +141 -19
  22. package/dist/ai/sessionManager.js.map +1 -1
  23. package/dist/ai/sessionRegistry.d.ts +57 -6
  24. package/dist/ai/sessionRegistry.d.ts.map +1 -1
  25. package/dist/ai/sessionRegistry.js +237 -31
  26. package/dist/ai/sessionRegistry.js.map +1 -1
  27. package/dist/config.d.ts +41 -0
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js +86 -0
  30. package/dist/config.js.map +1 -1
  31. package/dist/connection/connection.d.ts +61 -0
  32. package/dist/connection/connection.d.ts.map +1 -1
  33. package/dist/connection/connection.js +274 -1
  34. package/dist/connection/connection.js.map +1 -1
  35. package/dist/connection/relay.d.ts +20 -0
  36. package/dist/connection/relay.d.ts.map +1 -1
  37. package/dist/connection/relay.js +62 -1
  38. package/dist/connection/relay.js.map +1 -1
  39. package/dist/handlers/messageHandler.d.ts +10 -0
  40. package/dist/handlers/messageHandler.d.ts.map +1 -1
  41. package/dist/handlers/messageHandler.js +59 -3
  42. package/dist/handlers/messageHandler.js.map +1 -1
  43. package/dist/index.js +60 -2
  44. package/dist/index.js.map +1 -1
  45. package/dist/service/ipcClient.js +1 -1
  46. package/dist/service/ipcServer.d.ts.map +1 -1
  47. package/dist/service/ipcServer.js +10 -4
  48. package/dist/service/ipcServer.js.map +1 -1
  49. package/dist/service/run.d.ts.map +1 -1
  50. package/dist/service/run.js +69 -21
  51. package/dist/service/run.js.map +1 -1
  52. package/dist/service/types.d.ts +15 -0
  53. package/dist/service/types.d.ts.map +1 -1
  54. package/dist/service/types.js +6 -2
  55. package/dist/service/types.js.map +1 -1
  56. package/dist/tombstoneCheck.d.ts +43 -0
  57. package/dist/tombstoneCheck.d.ts.map +1 -0
  58. package/dist/tombstoneCheck.js +96 -0
  59. package/dist/tombstoneCheck.js.map +1 -0
  60. package/package.json +3 -3
@@ -15,16 +15,25 @@
15
15
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16
16
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
17
  import { execFileSync } from 'child_process';
18
- import { realpathSync, existsSync } from 'fs';
18
+ import { realpathSync, existsSync, readFileSync } from 'fs';
19
19
  import { join, dirname } from 'path';
20
+ import { homedir, platform } from 'os';
20
21
  import { fileURLToPath } from 'url';
21
22
  import { z } from 'zod';
22
- import { platform } from 'os';
23
23
  const __ownDir = dirname(fileURLToPath(import.meta.url));
24
- const cwdArg = process.argv.indexOf('--cwd');
25
- const cwd = cwdArg !== -1 ? process.argv[cwdArg + 1] : process.cwd();
24
+ function readArg(flag) {
25
+ const idx = process.argv.indexOf(flag);
26
+ return idx !== -1 ? process.argv[idx + 1] : undefined;
27
+ }
28
+ const cwd = readArg('--cwd') ?? process.cwd();
29
+ /** Known only on resume; undefined when the CLI spawns this MCP for a fresh session. */
30
+ const sessionIdHint = readArg('--session-id');
26
31
  const realCwd = realpathSync(cwd);
27
32
  const realHome = realpathSync(process.env.HOME ?? '/');
33
+ const quicksaveHome = process.env.QUICKSAVE_HOME || join(homedir(), '.quicksave');
34
+ const sessionRegistryDir = join(quicksaveHome, 'state', 'session-registry');
35
+ /** Same encoding as `apps/agent/src/ai/sessionRegistry.ts:encodeProjectPath`. */
36
+ const encodedCwd = cwd.replace(/\//g, '-');
28
37
  const PROFILE_PATH = join(__ownDir, 'profiles', 'project-sandbox.sb');
29
38
  function detectBackend() {
30
39
  const os = platform();
@@ -111,12 +120,97 @@ server.tool('SandboxBash', 'Execute a shell command WITHOUT requiring user permi
111
120
  };
112
121
  }
113
122
  });
114
- server.tool('SetTitle', 'Update the session title shown to the user. ' +
115
- 'Call this whenever you start a new task, switch context, or make meaningful progress. ' +
116
- 'Keep titles short and descriptive (e.g. "Fixing auth middleware", "Adding unit tests for UserService").', {
117
- title: z.string().describe('Short description of what you are currently doing'),
123
+ function readStoredStatus() {
124
+ const empty = {
125
+ subject: null, stage: null, blocked: null, note: null, recentNotes: [], source: 'unknown',
126
+ };
127
+ if (!sessionIdHint)
128
+ return empty;
129
+ const path = join(sessionRegistryDir, encodedCwd, `${sessionIdHint}.json`);
130
+ if (!existsSync(path))
131
+ return empty;
132
+ try {
133
+ const entry = JSON.parse(readFileSync(path, 'utf-8'));
134
+ const history = Array.isArray(entry.noteHistory) ? entry.noteHistory : [];
135
+ return {
136
+ subject: typeof entry.title === 'string' ? entry.title : null,
137
+ stage: entry.stage ?? null,
138
+ blocked: typeof entry.blocked === 'boolean' ? entry.blocked : null,
139
+ note: typeof entry.note === 'string' ? entry.note : null,
140
+ recentNotes: history.slice(-5),
141
+ source: 'stored',
142
+ };
143
+ }
144
+ catch {
145
+ return empty;
146
+ }
147
+ }
148
+ server.tool('UpdateSessionStatus', 'Update the ticket-style status for the current session, shown to the user on the home screen.\n' +
149
+ '\n' +
150
+ 'Call at the START of every session (first response), and again whenever stage changes, ' +
151
+ 'work gets blocked/unblocked, or progress is worth surfacing.\n' +
152
+ '\n' +
153
+ 'Call with NO fields to read the current stored status without mutating ' +
154
+ '(dry-run). Useful on resume to check whether subject/stage already match the work.\n' +
155
+ '\n' +
156
+ 'Fields:\n' +
157
+ ' subject — What this session is solving, from the user\'s perspective.\n' +
158
+ ' Good: "Fix auth token expiring early" Bad: "Debugging jwt.ts"\n' +
159
+ ' stage — Ticket lifecycle stage:\n' +
160
+ ' investigating reading code, finding root cause, understanding problem, designing\n' +
161
+ ' working actively writing or changing code\n' +
162
+ ' verifying running tests, confirming the fix, waiting on CI\n' +
163
+ ' done user-visible deliverable is complete\n' +
164
+ ' blocked — Orthogonal flag. Set true when stuck (waiting on user decision, ' +
165
+ 'permission request, external service). Set false when unblocked. Do not change stage.\n' +
166
+ ' note — One progress/finding entry, max ~12 words. APPENDED to the session\'s event log ' +
167
+ '(not overwritten). Emit one on meaningful state changes: ruling out a hypothesis, ' +
168
+ 'completing a sub-goal, hitting a blocker, starting verification, etc. ' +
169
+ 'Examples: "handler done, writing tests" / "permission pending on git push" / ' +
170
+ '"ruled out jwt.ts — secret looks right".\n' +
171
+ '\n' +
172
+ 'Typical flows:\n' +
173
+ ' Bug / debug: investigating → working → verifying → (loop or done)\n' +
174
+ ' Feature: investigating → working → verifying → done\n' +
175
+ ' Question: investigating → done\n' +
176
+ ' Chore: working → verifying → done\n' +
177
+ '\n' +
178
+ 'Rules:\n' +
179
+ '- Always call on the first response of a new session to set `subject` and `stage`.\n' +
180
+ '- On resume, if you cannot see a prior UpdateSessionStatus tool call in conversation ' +
181
+ 'history, do a no-args dry-run first; then set/correct subject or stage if blank or ' +
182
+ 'drifted from the current work.\n' +
183
+ '- For long-running tasks (research, large refactors), emit a `note` every time you ' +
184
+ 'cross a sub-goal or learn something — the user opens the session to skim recent notes ' +
185
+ 'as progress signal.\n' +
186
+ '- Do not skip `verifying` if you ran tests / build / repro.\n' +
187
+ '- Do not declare `done` until the user\'s problem is fully resolved.\n' +
188
+ '- For long refactors, prefer proposing to split into per-phase sub-sessions ' +
189
+ 'over keeping one session in `investigating` for a long time.', {
190
+ subject: z.string().optional().describe('Subject line — what this session is solving'),
191
+ stage: z.enum(['investigating', 'working', 'verifying', 'done']).optional()
192
+ .describe('Current lifecycle stage'),
193
+ blocked: z.boolean().optional().describe('True when stuck, false when unblocked'),
194
+ note: z.string().optional()
195
+ .describe('One progress/finding entry (~12 words). Appended to the session event log; emit on each meaningful state change.'),
118
196
  }, async (args) => {
119
- return { content: [{ type: 'text', text: `Title set: ${args.title}` }] };
197
+ const isDryRun = args.subject === undefined &&
198
+ args.stage === undefined &&
199
+ args.blocked === undefined &&
200
+ args.note === undefined;
201
+ // Daemon-side `shouldAutoApprove` has already persisted any updates by the
202
+ // time this handler runs, so re-reading the registry file yields the final
203
+ // merged state for both dry-run and write paths.
204
+ const snapshot = readStoredStatus();
205
+ const header = isDryRun
206
+ ? (snapshot.source === 'stored' ? 'Current session status (dry-run read):' : 'No stored status for this session yet.')
207
+ : 'Session status updated. Current status:';
208
+ return {
209
+ content: [{
210
+ type: 'text',
211
+ text: `${header}\n${JSON.stringify(snapshot, null, 2)}`,
212
+ }],
213
+ };
120
214
  });
121
215
  /** Quote a string for safe use inside a bash -c argument. */
122
216
  function shellQuote(s) {
@@ -1 +1 @@
1
- {"version":3,"file":"sandboxMcpStdio.js","sourceRoot":"","sources":["../../src/ai/sandboxMcpStdio.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sandboxMcpStdio.js","sourceRoot":"","sources":["../../src/ai/sandboxMcpStdio.ts"],"names":[],"mappings":"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"}
@@ -61,6 +61,10 @@ export declare class SessionManager extends EventEmitter {
61
61
  /** Send a raw control_request to the active provider session (CLI only). */
62
62
  sendControlRequest(sessionId: string, subtype: string, params?: Record<string, unknown>): Promise<unknown>;
63
63
  closeSession(sessionId: string): boolean;
64
+ /** Resolve and remove every pending user-input request for a session that
65
+ * is going away. Used by closeSession and onSessionExited so the map
66
+ * doesn't leak and any awaiter unblocks with a deny. */
67
+ private cancelPendingInputsForSession;
64
68
  setPermissionLevel(sessionId: string, level: PermissionLevel): Promise<boolean>;
65
69
  getPermissionLevel(sessionId: string): PermissionLevel;
66
70
  resolveUserInput(response: ClaudeUserInputResponsePayload): boolean;
@@ -110,8 +114,16 @@ export declare class SessionManager extends EventEmitter {
110
114
  makeCallbacks(agentId: AgentId): ProviderCallbacks;
111
115
  private handlePermissionRequest;
112
116
  private shouldAutoApprove;
113
- /** Update session title (triggered by SetTitle MCP tool). */
114
- private updateSessionTitle;
117
+ /**
118
+ * Update session ticket-model status (triggered by the UpdateSessionStatus MCP tool).
119
+ * Partial update: only fields present in `input` are written. Unknown-typed inputs
120
+ * are validated here before being applied to session config / registry.
121
+ *
122
+ * `note`, when supplied, is appended to the session's `noteHistory` log in the
123
+ * registry (capped at SESSION_NOTE_HISTORY_CAP, oldest-first). The latest note
124
+ * text is also mirrored to session config / registry `note` for quick access.
125
+ */
126
+ private updateSessionStatus;
115
127
  /** Move per-session state from oldId to newId after the CLI forks on
116
128
  * --resume. `sessionAgents[newId]` is already set by the caller; this
117
129
  * helper moves the remaining side maps and clears the old key. */
@@ -1 +1 @@
1
- {"version":3,"file":"sessionManager.d.ts","sourceRoot":"","sources":["../../src/ai/sessionManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessionManager.d.ts","sourceRoot":"","sources":["../../src/ai/sessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,KAAK,EACV,OAAO,EACP,oBAAoB,EACpB,6BAA6B,EAC7B,8BAA8B,EAC9B,iBAAiB,EACjB,WAAW,EAEX,mBAAmB,EAKnB,oBAAoB,EACrB,MAAM,2BAA2B,CAAC;AAOnC,OAAO,EAAE,iBAAiB,EAA6C,MAAM,kBAAkB,CAAC;AAIhG,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AA6DvB,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtC,yGAAyG;IACzG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAOD,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,kBAAkB,CAA2C;IACrE,OAAO,CAAC,gBAAgB,CAAmC;IAC3D,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,oBAAoB,CAA4C;IACxE,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,cAAc,CAAU;IAEhC,kGAAkG;IAClG,OAAO,CAAC,kBAAkB,CAAuD;gBAErE,SAAS,EAAE,mBAAmB,EAAE,EAAE,cAAc,GAAE,OAAuB;IAQrF,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,cAAc;IAsChB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,cAAc,IAAI,iBAAiB;IAInC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,iBAAiB;IAwBpE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC;IAIhE;oDACgD;IAChD,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAQ7D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IA8C1G,YAAY,CAAC,IAAI,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,GAAG,OAAO,CAAC,MAAM,CAAC;IAuEb,aAAa,CAAC,IAAI,EAAE;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,GAAG,OAAO,CAAC,MAAM,CAAC;IAsJb,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAaxD,4EAA4E;IACtE,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAYhH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAgBxC;;6DAEyD;IACzD,OAAO,CAAC,6BAA6B;IAU/B,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCrF,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAItD,gBAAgB,CAAC,QAAQ,EAAE,8BAA8B,GAAG,OAAO;IA+BnE,iBAAiB,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE;IAetJ,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIvC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIlC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD;;8DAE0D;IACpD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAO1H,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO;IAIzD,qBAAqB,IAAI,MAAM;IAI/B,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIrD,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IA2CnE,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAyEpG,uBAAuB,IAAI,6BAA6B,EAAE;IAI1D;;iEAE6D;IAC7D,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAOrD,aAAa;;;;;;;;;uBAnLqB,MAAM;iBAAO,MAAM;yBAAe,OAAO;6BAAmB,OAAO;4BAAkB,MAAM;wBAAc,OAAO;;;IA8LlJ,OAAO,IAAI,IAAI;IAiBf,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB;YAkDpC,uBAAuB;IAiFrC,OAAO,CAAC,iBAAiB;IAmCzB;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IAgE3B;;sEAEkE;IAClE,OAAO,CAAC,qBAAqB;IA4B7B,OAAO,CAAC,iBAAiB;IAIzB;;;;OAIG;IACH,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,oBAAoB;IA0ClE;;;;;;;;6EAQyE;IACzE,sBAAsB,IAAI,oBAAoB,EAAE;IAIhD,OAAO,CAAC,oBAAoB;YAiBd,mBAAmB;IAsBjC,OAAO,CAAC,gBAAgB;CAiBzB"}
@@ -1,9 +1,9 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import { resolve, join } from 'path';
3
3
  import { readFile, writeFile, mkdir } from 'fs/promises';
4
- import { DEFAULT_AGENT, DEFAULT_MODEL, matchAllowPattern, } from '@sumicom/quicksave-shared';
4
+ import { DEFAULT_AGENT, DEFAULT_MODEL, SESSION_NOTE_HISTORY_CAP, matchAllowPattern, } from '@sumicom/quicksave-shared';
5
5
  import { StreamCardBuilder, buildCardsFromHistory, loadPersistedCards } from './cardBuilder.js';
6
- import { SANDBOX_BASH_TOOL, SET_TITLE_TOOL } from './sandboxMcp.js';
6
+ import { SANDBOX_BASH_TOOL, UPDATE_SESSION_STATUS_TOOL } from './sandboxMcp.js';
7
7
  import { getSessionRegistry } from './sessionRegistry.js';
8
8
  import { getEventStore } from '../storage/eventStore.js';
9
9
  /**
@@ -32,12 +32,23 @@ const AUTO_APPROVE = {
32
32
  auto: new Set(['TodoWrite', 'EnterWorktree', 'ExitWorktree', 'Agent', 'EnterPlanMode']),
33
33
  };
34
34
  const DEFAULT_SYSTEM_PROMPT = [
35
- 'For non-destructive shell commands (ls, cat, find, git log, git status, git diff, etc.), prefer SandboxBash over Bash. SandboxBash runs in a sandboxed environment. Use Bash only for commands that modify state.',
36
- 'Call SetTitle to set a descriptive session title when you start a new task or switch context. Keep titles short (e.g. "Fixing auth middleware", "Adding unit tests for UserService"). Update it when the focus changes.',
35
+ 'For non-destructive shell commands (ls, cat, find, git log, git status, git diff, etc.), prefer `mcp__quicksave-sandbox__SandboxBash` over Bash. SandboxBash runs in a sandboxed environment. Use Bash only for commands that modify state.',
36
+ // Ticket-model status updates. Sessions surface on the user\'s home screen as tickets; keep this metadata fresh.
37
+ 'Treat each session as a ticket. The `mcp__quicksave-sandbox__UpdateSessionStatus` MCP tool is already loaded and available — call it directly, do NOT use ToolSearch. On your FIRST response in a new session, call it with at minimum `subject` and `stage` before doing other work. `subject` is what the user is trying to solve (e.g. "Fix auth token expiring early"), not what you are doing (not "Debugging jwt.ts"). On RESUME, if you do not see a prior `mcp__quicksave-sandbox__UpdateSessionStatus` tool_use in the conversation history, call it ONCE with no arguments as a dry-run to read the current stored status; if the returned subject is empty OR does not match what the user is now asking for, follow up with a real call to set/correct it.',
38
+ 'Re-call `mcp__quicksave-sandbox__UpdateSessionStatus` whenever the stage changes (investigating → working → verifying → done), whenever work becomes blocked or unblocked (set `blocked` true/false without changing `stage`), or when a one-line `note` would give the user useful progress signal. `note` is an append-only event log — each call adds one entry — so for long-running tasks (research, large refactors) emit a fresh `note` every time you rule out an approach, cross a sub-goal, or hit a blocker. Do not skip `verifying` when you have tests/build/repro running. Do not declare `done` until the user\'s problem is fully resolved.',
37
39
  ].join('\n\n');
38
40
  function buildSystemPrompt(extra) {
39
41
  return extra ? `${DEFAULT_SYSTEM_PROMPT}\n\n${extra}` : DEFAULT_SYSTEM_PROMPT;
40
42
  }
43
+ const SESSION_STAGES = [
44
+ 'investigating',
45
+ 'working',
46
+ 'verifying',
47
+ 'done',
48
+ ];
49
+ function isSessionStage(value) {
50
+ return SESSION_STAGES.includes(value);
51
+ }
41
52
  function normalizeAgentId(value) {
42
53
  if (value === 'claude-code' || value === 'claude-cli' || value === 'claude-sdk') {
43
54
  return 'claude-code';
@@ -385,6 +396,11 @@ export class SessionManager extends EventEmitter {
385
396
  return false;
386
397
  console.log(`[session-manager] cancel session=${sessionId.slice(0, 8)}`);
387
398
  ps.providerSession.interrupt();
399
+ // The CLI abandons its can_use_tool RPC after interrupt, so any awaiter
400
+ // on our side would hang forever. Resolve outstanding permissions with
401
+ // deny: it unblocks the daemon promise, frees the map slot, and matches
402
+ // user intent ("stop" = don't do that thing).
403
+ this.cancelPendingInputsForSession(sessionId);
388
404
  return true;
389
405
  }
390
406
  /** Send a raw control_request to the active provider session (CLI only). */
@@ -408,9 +424,24 @@ export class SessionManager extends EventEmitter {
408
424
  ps.providerSession = null;
409
425
  }
410
426
  this.sessions.delete(sessionId);
427
+ // Drop any pending permission promises for this session — the CLI is dead
428
+ // so any later resolveUserInput would land on an orphan promise, and the
429
+ // map entry would leak forever.
430
+ this.cancelPendingInputsForSession(sessionId);
411
431
  this.emitSessionUpdate(sessionId);
412
432
  return true;
413
433
  }
434
+ /** Resolve and remove every pending user-input request for a session that
435
+ * is going away. Used by closeSession and onSessionExited so the map
436
+ * doesn't leak and any awaiter unblocks with a deny. */
437
+ cancelPendingInputsForSession(sessionId) {
438
+ for (const [requestId, pending] of this.pendingInputRequests) {
439
+ if (pending.request.sessionId !== sessionId)
440
+ continue;
441
+ pending.resolve({ sessionId, requestId, action: 'deny' });
442
+ this.pendingInputRequests.delete(requestId);
443
+ }
444
+ }
414
445
  // ── Permission ──
415
446
  async setPermissionLevel(sessionId, level) {
416
447
  const ps = this.sessions.get(sessionId);
@@ -432,7 +463,17 @@ export class SessionManager extends EventEmitter {
432
463
  throw err;
433
464
  }
434
465
  }
435
- this.emitSessionUpdate(sessionId);
466
+ if (ps) {
467
+ this.emitSessionUpdate(sessionId);
468
+ }
469
+ else {
470
+ // Inactive session: emitSessionUpdate would carry archived=true (since
471
+ // the session isn't in the in-memory map), which the PWA reads as a
472
+ // "session retired" signal and bounces the user off the page. Persist
473
+ // to the registry instead so the new mode applies on the next cold
474
+ // resume; the PWA's optimistic state already reflects the change.
475
+ this.persistRegistryField(sessionId, 'permissionMode', level);
476
+ }
436
477
  return true;
437
478
  }
438
479
  getPermissionLevel(sessionId) {
@@ -458,7 +499,12 @@ export class SessionManager extends EventEmitter {
458
499
  });
459
500
  }
460
501
  this.emit('user-input-resolved', { requestId: response.requestId, sessionId: pending.request.sessionId });
461
- this.emitSessionUpdate(pending.request.sessionId);
502
+ // Skip the session-update emit when the session is no longer in memory:
503
+ // buildSessionUpdatePayload would set archived=true and navigate the PWA
504
+ // off the page. Can happen if the CLI exited (or was rekeyed) between
505
+ // sending the permission request and the user responding.
506
+ if (ps)
507
+ this.emitSessionUpdate(pending.request.sessionId);
462
508
  return true;
463
509
  }
464
510
  // ── Query ──
@@ -505,8 +551,7 @@ export class SessionManager extends EventEmitter {
505
551
  }
506
552
  async listAvailableSessions(cwd) {
507
553
  const registry = getSessionRegistry();
508
- const registryEntries = registry.getEntriesForProject(cwd)
509
- .filter(entry => !entry.archived);
554
+ const registryEntries = registry.getEntriesForProject(cwd);
510
555
  const pendingSessionIds = new Set(Array.from(this.pendingInputRequests.values()).map(p => p.request.sessionId));
511
556
  const eventStore = getEventStore();
512
557
  return registryEntries.map(entry => {
@@ -668,6 +713,13 @@ export class SessionManager extends EventEmitter {
668
713
  handlePermissionRequest: async (sessionId, req) => {
669
714
  return this.handlePermissionRequest(sessionId, req);
670
715
  },
716
+ onToolUse: (sessionId, toolName, toolInput) => {
717
+ // Claude CLI's auto permission mode skips can_use_tool for MCP tools,
718
+ // so handlePermissionRequest can't reliably drive side effects here.
719
+ if (toolName === UPDATE_SESSION_STATUS_TOOL) {
720
+ this.updateSessionStatus(sessionId, toolInput);
721
+ }
722
+ },
671
723
  onModelDetected: (model) => {
672
724
  console.log(`[session-manager] model detected: ${model} (agent=${agentId})`);
673
725
  if (agentId === 'claude-code') {
@@ -684,6 +736,7 @@ export class SessionManager extends EventEmitter {
684
736
  ps.providerSession = null;
685
737
  ps.streaming = false;
686
738
  this.sessions.delete(sessionId);
739
+ this.cancelPendingInputsForSession(sessionId);
687
740
  this.emitSessionUpdate(sessionId);
688
741
  },
689
742
  };
@@ -754,10 +807,10 @@ export class SessionManager extends EventEmitter {
754
807
  return { action: 'allow' };
755
808
  }
756
809
  shouldAutoApprove(sessionId, toolName, input) {
757
- // SetTitle — always approve
758
- if (toolName === SET_TITLE_TOOL) {
759
- if (input.title)
760
- this.updateSessionTitle(sessionId, input.title);
810
+ // UpdateSessionStatus — always approve. The actual metadata write happens
811
+ // in the onToolUse callback (driven by the assistant stream), because
812
+ // CLI auto mode pre-approves MCP tools without ever sending can_use_tool.
813
+ if (toolName === UPDATE_SESSION_STATUS_TOOL) {
761
814
  return true;
762
815
  }
763
816
  // SandboxBash: auto-approve when sandboxed, otherwise check as Bash
@@ -784,18 +837,69 @@ export class SessionManager extends EventEmitter {
784
837
  }
785
838
  return true;
786
839
  }
787
- /** Update session title (triggered by SetTitle MCP tool). */
788
- updateSessionTitle(sessionId, title) {
789
- const config = this.sessionConfigs.get(sessionId) ?? {};
790
- this.sessionConfigs.set(sessionId, { ...config, title });
791
- this.emit('session-config-updated', { sessionId, config: this.sessionConfigs.get(sessionId) });
792
- // Update session registry so it persists across restarts
840
+ /**
841
+ * Update session ticket-model status (triggered by the UpdateSessionStatus MCP tool).
842
+ * Partial update: only fields present in `input` are written. Unknown-typed inputs
843
+ * are validated here before being applied to session config / registry.
844
+ *
845
+ * `note`, when supplied, is appended to the session's `noteHistory` log in the
846
+ * registry (capped at SESSION_NOTE_HISTORY_CAP, oldest-first). The latest note
847
+ * text is also mirrored to session config / registry `note` for quick access.
848
+ */
849
+ updateSessionStatus(sessionId, input) {
850
+ const configUpdates = {};
851
+ const entryUpdates = {};
852
+ let noteToAppend = null;
853
+ let changed = false;
854
+ if (typeof input.subject === 'string' && input.subject.length > 0) {
855
+ configUpdates.title = input.subject;
856
+ entryUpdates.title = input.subject;
857
+ changed = true;
858
+ }
859
+ if (typeof input.stage === 'string' && isSessionStage(input.stage)) {
860
+ configUpdates.stage = input.stage;
861
+ entryUpdates.stage = input.stage;
862
+ changed = true;
863
+ }
864
+ if (typeof input.blocked === 'boolean') {
865
+ configUpdates.blocked = input.blocked;
866
+ entryUpdates.blocked = input.blocked;
867
+ changed = true;
868
+ }
869
+ if (typeof input.note === 'string' && input.note.length > 0) {
870
+ configUpdates.note = input.note;
871
+ entryUpdates.note = input.note;
872
+ noteToAppend = input.note;
873
+ changed = true;
874
+ }
875
+ if (!changed)
876
+ return;
877
+ const prev = this.sessionConfigs.get(sessionId) ?? {};
878
+ const next = { ...prev, ...configUpdates };
879
+ this.sessionConfigs.set(sessionId, next);
880
+ this.emit('session-config-updated', { sessionId, config: next });
793
881
  const ps = this.sessions.get(sessionId);
794
882
  if (ps?.cwd) {
795
883
  const registry = getSessionRegistry();
796
884
  const entry = registry.getEntry(ps.cwd, sessionId);
797
885
  if (entry) {
798
- registry.upsertEntry({ ...entry, title, lastAccessedAt: Date.now() });
886
+ let nextHistory = entry.noteHistory;
887
+ if (noteToAppend !== null) {
888
+ const appended = { ts: Date.now(), text: noteToAppend };
889
+ const prior = Array.isArray(entry.noteHistory) ? entry.noteHistory : [];
890
+ const combined = [...prior, appended];
891
+ // Trim oldest-first once we exceed the cap so the registry payload
892
+ // stays broadcast-friendly.
893
+ nextHistory = combined.length > SESSION_NOTE_HISTORY_CAP
894
+ ? combined.slice(combined.length - SESSION_NOTE_HISTORY_CAP)
895
+ : combined;
896
+ }
897
+ registry.upsertEntry({
898
+ ...entry,
899
+ ...entryUpdates,
900
+ ...(nextHistory !== entry.noteHistory ? { noteHistory: nextHistory } : {}),
901
+ lastAccessedAt: Date.now(),
902
+ });
799
903
  }
800
904
  }
801
905
  }
@@ -818,6 +922,15 @@ export class SessionManager extends EventEmitter {
818
922
  this.sessionConfigs.set(newId, config);
819
923
  this.sessionConfigs.delete(oldId);
820
924
  }
925
+ // Rewrite any pending input request still tagged with the old sessionId
926
+ // so a later resolveUserInput can find the new session entry (and its
927
+ // cardBuilder) to clear the PWA's pending-permission UI. Without this,
928
+ // permission prompts emitted before the rekey stay stuck on screen.
929
+ for (const [, pending] of this.pendingInputRequests) {
930
+ if (pending.request.sessionId === oldId) {
931
+ pending.request = { ...pending.request, sessionId: newId };
932
+ }
933
+ }
821
934
  this.sessionAgents.delete(oldId);
822
935
  }
823
936
  emitSessionUpdate(sessionId) {
@@ -920,6 +1033,15 @@ export class SessionManager extends EventEmitter {
920
1033
  waitForUserInput(requestId, request) {
921
1034
  return new Promise((resolve) => {
922
1035
  this.pendingInputRequests.set(requestId, { resolve, request });
1036
+ // Notify subscribers so the PWA's dot indicator flips to "pending"
1037
+ // immediately. resolveUserInput emits its own update to flip it back.
1038
+ // Guard against an inactive session: emitting then would carry
1039
+ // archived=true and bounce the PWA off the page. (Should not normally
1040
+ // happen — the CLI is alive whenever it asks for permission — but the
1041
+ // race is cheap to guard.)
1042
+ if (this.sessions.has(request.sessionId)) {
1043
+ this.emitSessionUpdate(request.sessionId);
1044
+ }
923
1045
  });
924
1046
  }
925
1047
  }