claude-code-session-manager 0.112.0 → 0.114.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 (75) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/assets/{AgentLibrary-RwPvEgqm.js → AgentLibrary-DZFM7Lto.js} +2 -2
  3. package/dist/assets/{Button-36j3PzYv.js → Button-BJVFKdGH.js} +1 -1
  4. package/dist/assets/{DataModel-DUhANo4l.js → DataModel-CebvcP_y.js} +1 -1
  5. package/dist/assets/{History-BeZzNjmv.js → History-BwISEUhR.js} +2 -2
  6. package/dist/assets/{Hooks-iQ8845Hp.js → Hooks-Dac3smYP.js} +3 -3
  7. package/dist/assets/{Library-DPE1h-1L.js → Library-DTjsUr9W.js} +7 -7
  8. package/dist/assets/{ListDetail-PStOuQAo.js → ListDetail-B1L5TbjS.js} +1 -1
  9. package/dist/assets/{MarkdownEditor-DPw-N2-m.js → MarkdownEditor-BFRhWZD4.js} +1 -1
  10. package/dist/assets/{McpServers-CUyRTNep.js → McpServers-CBjGCMg0.js} +2 -2
  11. package/dist/assets/Memory-DdLi5fqe.js +8 -0
  12. package/dist/assets/{Panel-_VZ_uXNg.js → Panel-BQkkkjex.js} +1 -1
  13. package/dist/assets/{Permissions-PPRmfNbK.js → Permissions-CaACwY0B.js} +3 -3
  14. package/dist/assets/{Plugins-BFcvfvlx.js → Plugins-DJeBwVoh.js} +2 -2
  15. package/dist/assets/ProvenanceBadge--N5j8xiK.js +1 -0
  16. package/dist/assets/{RemoteMachines-D5V0lzgW.js → RemoteMachines-WjWaDHEA.js} +2 -2
  17. package/dist/assets/{SaveBar-Dw64FVdM.js → SaveBar-DFmOjb4_.js} +1 -1
  18. package/dist/assets/{Scheduler-C8s_pJ3I.js → Scheduler-Miap6_aY.js} +8 -8
  19. package/dist/assets/{ScopeSwitcher-D-nKDMAE.js → ScopeSwitcher-CiEujp4L.js} +1 -1
  20. package/dist/assets/Settings-UGpW4P0j.js +4 -0
  21. package/dist/assets/{SkillReferenceGraph-BbZFMHim.js → SkillReferenceGraph-BgyAnxGW.js} +1 -1
  22. package/dist/assets/Skills-BQUpnNfx.js +2 -0
  23. package/dist/assets/SystemPrompt-CQjOgHGT.js +1 -0
  24. package/dist/assets/TagLibrary-CTVpoLl0.js +1 -0
  25. package/dist/assets/{TiptapBody-Cr-hxX8L.js → TiptapBody-BtOcrdRR.js} +1 -1
  26. package/dist/assets/{Toggle-7WpPzcZH.js → Toggle-Chri4G19.js} +1 -1
  27. package/dist/assets/{ViewTabs-9lQCRwWJ.js → ViewTabs-BIenDGVv.js} +1 -1
  28. package/dist/assets/{historyTotals-BrFxw5J1.js → historyTotals-CX8mUxJY.js} +1 -1
  29. package/dist/assets/{index-CTBpf-26.js → index-AVrhGmUO.js} +163 -163
  30. package/dist/assets/{index-Dutcsly-.css → index-HpEbvDiE.css} +1 -1
  31. package/dist/assets/{settingsSchema-Dh5Kl7VO.js → settingsSchema-uqwlznbH.js} +1 -1
  32. package/dist/assets/{useAgentsDirWatch-CMpx0d04.js → useAgentsDirWatch-BaezRtWw.js} +1 -1
  33. package/dist/assets/{useRemotePeers-CI2axd-L.js → useRemotePeers-BuFbBqDt.js} +1 -1
  34. package/dist/index.html +2 -2
  35. package/package.json +1 -1
  36. package/plugins/session-manager-dev/.claude-plugin/plugin.json +1 -1
  37. package/plugins/session-manager-dev/skills/send-feedback/SKILL.md +13 -0
  38. package/scripts/scheduler-mcp-server.cjs +23 -5
  39. package/src/main/build-info.json +4 -4
  40. package/src/main/chatRunner.cjs +18 -1
  41. package/src/main/config.cjs +18 -1
  42. package/src/main/index.cjs +6 -2
  43. package/src/main/ipcSchemas.cjs +27 -34
  44. package/src/main/lib/activeSessions.cjs +13 -0
  45. package/src/main/lib/claude/rateLimitDetect.cjs +29 -10
  46. package/src/main/lib/claude/rateLimitWindow.cjs +23 -11
  47. package/src/main/lib/credentials.cjs +16 -90
  48. package/src/main/lib/feedback-remote/crossProjectFeedback.cjs +107 -9
  49. package/src/main/lib/feedback-remote/humanPromptLedger.cjs +93 -0
  50. package/src/main/lib/mcpToolCatalog.cjs +9 -3
  51. package/src/main/lib/memoryDirResolve.cjs +139 -0
  52. package/src/main/lib/memoryIndex.cjs +98 -0
  53. package/src/main/lib/prd/depSlugResolve.cjs +16 -1
  54. package/src/main/lib/scheduler/finalizeReason.cjs +30 -0
  55. package/src/main/lib/scheduler/flatSweepThrottle.cjs +17 -0
  56. package/src/main/lib/scheduler/runLogRetention.cjs +34 -13
  57. package/src/main/lib/scheduler/schedulerBatch.cjs +8 -12
  58. package/src/main/lib/telemetry/telemetryClient.cjs +8 -4
  59. package/src/main/lib/util/atomicFs.cjs +42 -0
  60. package/src/main/lib/util/loopDelay.cjs +1 -1
  61. package/src/main/memoryAggregate.cjs +15 -8
  62. package/src/main/memoryTool.cjs +151 -22
  63. package/src/main/runVerify.cjs +5 -2
  64. package/src/main/scheduler.cjs +43 -10
  65. package/src/main/supervisor.cjs +45 -22
  66. package/src/main/webRemote.cjs +15 -0
  67. package/src/preload/api.d.ts +47 -52
  68. package/src/preload/index.cjs +25 -21
  69. package/dist/assets/Memory-bZ2xjBGy.js +0 -8
  70. package/dist/assets/ProvenanceBadge-B1WUknak.js +0 -1
  71. package/dist/assets/Settings-CQSrR4mO.js +0 -4
  72. package/dist/assets/Skills-Bzyrh6EQ.js +0 -2
  73. package/dist/assets/SystemPrompt-DP6i3Dx0.js +0 -1
  74. package/dist/assets/TagLibrary-iip0C6Bl.js +0 -1
  75. package/src/main/agentMemory.cjs +0 -246
@@ -65,6 +65,8 @@ const { headlessPermissionArgs, ensureCliCapsProbed } = require('./lib/claude/cl
65
65
  // as the authoritative guarantee.
66
66
  ensureCliCapsProbed().catch(() => {});
67
67
  const launchFailure = require('./lib/scheduler/launchFailure.cjs');
68
+ const { finalizeReasonFor } = require('./lib/scheduler/finalizeReason.cjs');
69
+ const { shouldRunFlatSweep, FLAT_SWEEP_INTERVAL_MS } = require('./lib/scheduler/flatSweepThrottle.cjs');
68
70
  const { appendError } = require('./lib/telemetry/opsErrorLog.cjs');
69
71
  const { readTail } = require('./lib/util/fileTail.cjs');
70
72
  const {
@@ -1389,6 +1391,9 @@ function reconcileSourcePromptId(job, parsedSourcePromptId) {
1389
1391
  // process rather than one per 60s reconcile tick.
1390
1392
  const warnedUnregisteredEpics = new Set();
1391
1393
 
1394
+ /** @type {number | null} last flat-sweep start (ms); null until first pass. */
1395
+ let lastFlatSweepMs = null;
1396
+
1392
1397
  async function reconcile(state) {
1393
1398
  // Defence in depth — tickQueue already gates on this, but reconcile is the
1394
1399
  // function that would do the damage (every unmatched PRD .md becomes a
@@ -1414,8 +1419,17 @@ async function reconcile(state) {
1414
1419
  // instead of ever reaching the onDisk scan below.
1415
1420
  let phaseStartMs = Date.now();
1416
1421
  const skipCwds = quarantinedCwdSet(state);
1417
- await prdPaths.consolidateAllFlatPrds(allProjectCwds(), skipCwds);
1418
- phaseMs.flatPrdSweep = Date.now() - phaseStartMs;
1422
+ // Throttled: the flat layout is retired, and the sweep (plus the ~270 ms
1423
+ // allProjectCwds() scan) cost ~1 s per pass. A PRD dropped into a flat dir
1424
+ // is still consolidated within one FLAT_SWEEP_INTERVAL_MS; the first pass
1425
+ // after boot always runs.
1426
+ if (shouldRunFlatSweep(phaseStartMs, lastFlatSweepMs, FLAT_SWEEP_INTERVAL_MS)) {
1427
+ lastFlatSweepMs = phaseStartMs;
1428
+ await prdPaths.consolidateAllFlatPrds(allProjectCwds(), skipCwds);
1429
+ phaseMs.flatPrdSweep = Date.now() - phaseStartMs;
1430
+ } else {
1431
+ phaseMs.flatPrdSweep = 0;
1432
+ }
1419
1433
 
1420
1434
  phaseStartMs = Date.now();
1421
1435
  const { files, dirCount } = await listPrdFiles(skipCwds);
@@ -2048,18 +2062,34 @@ function getNextResetCached() {
2048
2062
  * (usageCircuit.isResetFresh): a stale reset must never arm a resume timer
2049
2063
  * that already elapsed (that's the 30-second-nap bug computeEffectiveResumeAt
2050
2064
  * below also guards against), so a stale value here returns null and lets
2051
- * the caller's 30-minute fallback take over instead.
2065
+ * the caller's 30-minute fallback take over instead. The billing fallback is
2066
+ * used only when its binding reading is at/above `threshold` — a 58% weekly
2067
+ * reading once paused the queue to the weekly reset 4 days out (2026-10-11);
2068
+ * an unknown/non-finite reading returns null so the 30-minute fallback applies.
2052
2069
  */
2053
- function resolveRateLimitPauseReset(logPath, billingResetIso, nowMs = Date.now()) {
2070
+ function resolveRateLimitPauseReset(logPath, billingResetIso, nowMs = Date.now(), { billingUtilization, threshold } = {}) {
2054
2071
  const logReset = resolveBindingRateLimitReset(logPath);
2055
2072
  if (logReset != null) {
2056
2073
  const iso = new Date(logReset * 1000).toISOString();
2057
2074
  if (isResetFresh(iso, nowMs)) return iso;
2058
2075
  }
2059
- if (billingResetIso && isResetFresh(billingResetIso, nowMs)) return billingResetIso;
2076
+ if (
2077
+ billingResetIso
2078
+ && Number.isFinite(billingUtilization)
2079
+ && Number.isFinite(threshold)
2080
+ && billingUtilization >= threshold
2081
+ && isResetFresh(billingResetIso, nowMs)
2082
+ ) return billingResetIso;
2060
2083
  return null;
2061
2084
  }
2062
2085
 
2086
+ /** Logs when a fresh billing reset was discarded because its reading is under threshold. */
2087
+ function logBillingResetRejected(billingResetIso, billingUtilization, threshold) {
2088
+ if (!billingResetIso || !Number.isFinite(threshold)) return;
2089
+ if (Number.isFinite(billingUtilization) && billingUtilization >= threshold) return;
2090
+ console.log(`[scheduler] rate_limit pause: billing binding window at ${Number.isFinite(billingUtilization) ? `${billingUtilization}%` : 'unknown'} < ${threshold}% — using 30-min fallback, not ${billingResetIso}`);
2091
+ }
2092
+
2063
2093
  /**
2064
2094
  * Shared by both rate-limit pause sites (spawnJob's rateLimited branch and
2065
2095
  * reapDeadRunningJobs): the billing-endpoint-derived fallback reset used
@@ -6077,7 +6107,10 @@ async function spawnJob(job, runId, runDir, defaultCwd, resumeTarget = null, sib
6077
6107
  billing.usageCircuit.recordFailure('executor_429');
6078
6108
  const logPath = path.join(runDir, `${job.slug}.log`);
6079
6109
  const billingResetIso = await billingResetForPause();
6080
- const resetIso = resolveRateLimitPauseReset(logPath, billingResetIso);
6110
+ const pauseState = await readQueue().catch(() => null);
6111
+ const pauseThreshold = pauseState?.config?.utilizationThreshold;
6112
+ const resetIso = resolveRateLimitPauseReset(logPath, billingResetIso, Date.now(), { billingUtilization: cachedUtilization, threshold: pauseThreshold });
6113
+ if (resetIso == null) logBillingResetRejected(billingResetIso, cachedUtilization, pauseThreshold);
6081
6114
  const observedAt = dispatchStartedAtMs;
6082
6115
  const prevCount = consecutiveRapidRateLimitsBySlug.get(job.slug) || 0;
6083
6116
  const nextCount = nextRapidRateLimitCount(prevCount, { rateLimited: true, durationMs: res.durationMs });
@@ -6570,9 +6603,7 @@ async function spawnJob(job, runId, runDir, defaultCwd, resumeTarget = null, sib
6570
6603
  // reason — surface that on the completed row's statusHistory
6571
6604
  // instead of the generic "run finished with exit 0" every other
6572
6605
  // completed run gets.
6573
- const finalizeReason = (effectiveStatus === 'completed' && verifyResult?.verdict === 'already_satisfied_on_main')
6574
- ? verifyResult.reason
6575
- : (sigtermOverrideReason ?? `run finished with exit ${res.exitCode}`);
6606
+ const finalizeReason = finalizeReasonFor({ effectiveStatus, verifyResult, sigtermOverrideReason, exitCode: res.exitCode });
6576
6607
  // error/verifierVerdict are stamped BEFORE transitionJob() below —
6577
6608
  // needsReviewLedger's buildNeedsReviewEntryLine reads job.
6578
6609
  // verifierVerdict/heldReason/error synchronously off `job` the
@@ -7875,7 +7906,9 @@ async function reapDeadRunningJobs() {
7875
7906
  billing.usageCircuit.recordFailure('executor_429');
7876
7907
  const triggering = dead.find((d) => d.outcome === 'rate_limited');
7877
7908
  const billingResetIso = await billingResetForPause();
7878
- const resetIso = resolveRateLimitPauseReset(triggering.logPath, billingResetIso);
7909
+ const pauseThreshold = state.config?.utilizationThreshold;
7910
+ const resetIso = resolveRateLimitPauseReset(triggering.logPath, billingResetIso, Date.now(), { billingUtilization: cachedUtilization, threshold: pauseThreshold });
7911
+ if (resetIso == null) logBillingResetRejected(billingResetIso, cachedUtilization, pauseThreshold);
7879
7912
  const triggeringRow = triggering ? state.jobs.find((x) => x.slug === triggering.slug) : null;
7880
7913
  const observedAtMs = triggeringRow?.startedAt ? Date.parse(triggeringRow.startedAt) : null;
7881
7914
  // Same rapid-repeat circuit breaker spawnJob's own res.rateLimited
@@ -205,6 +205,41 @@ Decision rules:
205
205
  - "stuck"/kill-agent only if the agent itself is wedged (no child processes, no recent log events, > 60 min stale). Set targetPid to the claude root pid.`;
206
206
  }
207
207
 
208
+ /**
209
+ * Parse `claude -p --output-format json` stdout from a probe.
210
+ * @param {string} stdout
211
+ * @returns {{ verdictObj: any, costUsd: number|null, errorSubtype: string|null }}
212
+ */
213
+ function parseProbeOutput(stdout) {
214
+ let costUsd = null;
215
+ let verdictObj = null;
216
+ let errorSubtype = null;
217
+
218
+ // --output-format json emits a single JSON result object.
219
+ // The `result` field contains the assistant's raw text response.
220
+ for (const line of String(stdout).split('\n').reverse()) {
221
+ const trimmed = line.trim();
222
+ if (!trimmed) continue;
223
+ try {
224
+ const obj = JSON.parse(trimmed);
225
+ if (!obj || typeof obj !== 'object') continue;
226
+ if (typeof obj.total_cost_usd === 'number') costUsd = obj.total_cost_usd;
227
+ if (obj.is_error === true || (typeof obj.subtype === 'string' && obj.subtype !== 'success')) {
228
+ errorSubtype = typeof obj.subtype === 'string' && obj.subtype ? obj.subtype : 'error';
229
+ }
230
+ // Extract verdict from the result text field (assistant's response).
231
+ if (obj.result && typeof obj.result === 'string') {
232
+ const m = obj.result.match(/\{[^{}]+\}/);
233
+ if (m) {
234
+ try { verdictObj = JSON.parse(m[0]); } catch { /* */ }
235
+ }
236
+ }
237
+ } catch { /* not JSON */ }
238
+ if (verdictObj) break;
239
+ }
240
+ return { verdictObj, costUsd, errorSubtype };
241
+ }
242
+
208
243
  async function runProbe(claudeBin, prompt) {
209
244
  await ensureCliCapsProbed();
210
245
  return new Promise((resolve) => {
@@ -213,10 +248,10 @@ async function runProbe(claudeBin, prompt) {
213
248
  const target = claudeSpawnTarget('aux', 'supervisor', claudeBin);
214
249
  child = spawn(target.command, [
215
250
  '-p', prompt,
216
- '--model', 'claude-opus-4-7',
251
+ '--model', 'claude-haiku-5-5',
217
252
  '--no-session-persistence',
218
253
  '--output-format', 'json',
219
- '--max-budget-usd', '0.10',
254
+ '--max-budget-usd', '0.50',
220
255
  '--dangerously-skip-permissions',
221
256
  ...headlessPermissionArgs(),
222
257
  '--allowedTools', 'Bash',
@@ -254,26 +289,13 @@ async function runProbe(claudeBin, prompt) {
254
289
  child.on('exit', () => {
255
290
  clearTimers();
256
291
  const stdout = Buffer.concat(stdoutChunks).toString('utf8');
257
- let costUsd = null;
258
- let verdictObj = null;
259
-
260
- // --output-format json emits a single JSON result object.
261
- // The `result` field contains the assistant's raw text response.
262
- for (const line of stdout.split('\n').reverse()) {
263
- const trimmed = line.trim();
264
- if (!trimmed) continue;
265
- try {
266
- const obj = JSON.parse(trimmed);
267
- if (typeof obj.total_cost_usd === 'number') costUsd = obj.total_cost_usd;
268
- // Extract verdict from the result text field (assistant's response).
269
- if (obj.result && typeof obj.result === 'string') {
270
- const m = obj.result.match(/\{[^{}]+\}/);
271
- if (m) {
272
- try { verdictObj = JSON.parse(m[0]); } catch { /* */ }
273
- }
274
- }
275
- } catch { /* not JSON */ }
276
- if (verdictObj) break;
292
+ const { verdictObj, costUsd, errorSubtype } = parseProbeOutput(stdout);
293
+
294
+ if ((!verdictObj || !verdictObj.verdict) && errorSubtype) {
295
+ // Budget cap / CLI error exit: record it distinctly, never as a real verdict.
296
+ console.warn('[supervisor] probe error exit (treating as ok):', errorSubtype);
297
+ resolve({ verdict: 'ok', action: 'none', targetPid: null, reason: `probe_error:${errorSubtype}`, costUsd });
298
+ return;
277
299
  }
278
300
 
279
301
  if (!verdictObj || !verdictObj.verdict) {
@@ -506,4 +528,5 @@ module.exports = {
506
528
  supervisorTick,
507
529
  applyAction,
508
530
  registerHandlers,
531
+ parseProbeOutput,
509
532
  };
@@ -83,6 +83,7 @@ let _pongTimer = null;
83
83
  let _missedPongs = 0;
84
84
  let _configCache = null;
85
85
  let _configCacheAt = 0;
86
+ let _repairNeeded = false; // relay rejected the device token (invalid_token / 401): terminal until re-pair
86
87
  let _destroyed = false; // set at app shutdown to stop reconnect loops
87
88
 
88
89
  // E2E session state — reset on each new WS connection.
@@ -274,6 +275,7 @@ function computeStatus(cfg) {
274
275
  enabled: cfg.remoteEnabled,
275
276
  remoteControlEnabled: cfg.remoteControlEnabled ?? false,
276
277
  connected,
278
+ linkState: _repairNeeded ? 'repair-needed' : 'ok',
277
279
  e2eActive: connected && _clients.values().some((r) => r.e2e.sessionKey !== null),
278
280
  e2eAuthenticated: connected && authed !== null,
279
281
  e2eState: connected ? e2eState : 'idle',
@@ -389,9 +391,18 @@ async function connect() {
389
391
  scope: 'webRemote', level: 'warn',
390
392
  message: 'device ticket request failed', meta: { error: e?.message },
391
393
  });
394
+ // invalid_token / 401 is final (device unpaired server-side): retrying only
395
+ // spams the relay. Park in repair-needed until a new pair stores a fresh token.
396
+ if (e?.message === 'invalid_token' || e?.message === 'HTTP 401') {
397
+ cancelReconnect();
398
+ _repairNeeded = true;
399
+ broadcastStatus();
400
+ return;
401
+ }
392
402
  scheduleReconnect();
393
403
  return;
394
404
  }
405
+ _repairNeeded = false;
395
406
 
396
407
  // Step 2: open WSS connection with the ticket.
397
408
  let ws;
@@ -1278,6 +1289,7 @@ async function pair(otp) {
1278
1289
  }];
1279
1290
 
1280
1291
  await saveConfig({ ...cfg, devices });
1292
+ _repairNeeded = false;
1281
1293
 
1282
1294
  if (cfg.remoteEnabled) {
1283
1295
  connect().catch(() => {});
@@ -1480,6 +1492,9 @@ module.exports = {
1480
1492
  getDispatchMap,
1481
1493
  buildRemoteSessionList,
1482
1494
  setWs: (ws) => { _ws = ws; },
1495
+ connect,
1496
+ hasReconnectTimer: () => _reconnectTimer !== null,
1497
+ cancelReconnect,
1483
1498
  invalidateConfigCache,
1484
1499
  setE2eAuthenticated: (key, clientKey = LEGACY_CLIENT_KEY) => {
1485
1500
  _clients.getOrCreate(clientKey).e2e = makeState('authenticated', key, null);
@@ -1259,9 +1259,41 @@ export interface MemoryEntry {
1259
1259
  bytes: number;
1260
1260
  }
1261
1261
 
1262
+ export interface MemoryAgentTarget {
1263
+ name: string;
1264
+ scope: 'user' | 'project' | 'local';
1265
+ }
1266
+
1267
+ /** Addressing for memory:* calls. `agent` targets an agent's native memory dir; `cwd` drives native resolution. */
1268
+ export interface MemoryTarget {
1269
+ workspace?: string;
1270
+ cwd?: string;
1271
+ agent?: MemoryAgentTarget;
1272
+ }
1273
+
1274
+ export interface MemoryAgent {
1275
+ name: string;
1276
+ origin: 'user' | 'project';
1277
+ memoryScope: 'user' | 'project' | 'local' | null;
1278
+ agentFile: string;
1279
+ }
1280
+
1281
+ export interface MemoryIndexHealth {
1282
+ lines: number;
1283
+ bytes: number;
1284
+ maxLines: number;
1285
+ maxBytes: number;
1286
+ overBudget: boolean;
1287
+ nearBudget: boolean;
1288
+ orphans: string[];
1289
+ dangling: string[];
1290
+ }
1291
+
1262
1292
  export interface MemoryListResult {
1263
1293
  entries: MemoryEntry[];
1264
1294
  workspace: string;
1295
+ dir: string;
1296
+ autoMemoryEnabled: boolean;
1265
1297
  error: string | null;
1266
1298
  }
1267
1299
 
@@ -1314,6 +1346,7 @@ export interface MemoryStaleResult {
1314
1346
  entries: MemoryStaleEntry[];
1315
1347
  workspace: string;
1316
1348
  error: string | null;
1349
+ index?: MemoryIndexHealth;
1317
1350
  }
1318
1351
 
1319
1352
  // ────────────────────────────────────────────── Project Brief (PRD 837)
@@ -1402,37 +1435,6 @@ export interface ProjectPagesGetResult {
1402
1435
  demoVideo: { path: string; mtimeMs: number } | null;
1403
1436
  }
1404
1437
 
1405
- // ────────────────────────────────────────────── Per-subagent memory
1406
- // Stored at ~/.claude/session-manager/agent-memory/<agentId>.json. Keyed by
1407
- // agent name (the .md filename in ~/.claude/agents/), not by workspace cwd.
1408
-
1409
- export type AgentMemoryCategory = 'command' | 'preference' | 'pattern' | 'failure' | 'workflow';
1410
-
1411
- export interface AgentMemoryEntry {
1412
- id: string;
1413
- body: string;
1414
- category: AgentMemoryCategory | null;
1415
- createdAt: number;
1416
- updatedAt: number;
1417
- bytes: number;
1418
- }
1419
-
1420
- export interface AgentMemoryListResult {
1421
- entries: AgentMemoryEntry[];
1422
- agentId: string;
1423
- error: string | null;
1424
- }
1425
-
1426
- export interface AgentMemoryGetResult {
1427
- entry: AgentMemoryEntry | null;
1428
- error: string | null;
1429
- }
1430
-
1431
- export interface AgentMemoryMutationResult {
1432
- ok: boolean;
1433
- error: string | null;
1434
- }
1435
-
1436
1438
  // ────────────────────────────────────────────── Git status (richer than app:git-branch)
1437
1439
 
1438
1440
  /** Mirrors the status returned by src/main/git.cjs mapStatus(). */
@@ -1495,6 +1497,8 @@ export interface ChatRunPayload {
1495
1497
  /** Originating PromptTicket.id (PRD 748) when dequeued from a tab's
1496
1498
  * prompt queue — absent for a fresh manual send. */
1497
1499
  promptId?: string;
1500
+ /** Who wrote the prompt: a local human, an injected external source, or an Epic proposal from another project. */
1501
+ origin?: 'human' | 'external' | 'inbound-proposal';
1498
1502
  }
1499
1503
 
1500
1504
  export interface ChatCreatePrdPayload {
@@ -1649,6 +1653,8 @@ export interface WebRemoteStatus {
1649
1653
  enabled: boolean;
1650
1654
  remoteControlEnabled: boolean;
1651
1655
  connected: boolean;
1656
+ /** 'repair-needed' once the relay rejected the device token; the device must be re-paired. */
1657
+ linkState?: 'ok' | 'repair-needed';
1652
1658
  e2eActive: boolean;
1653
1659
  /** True once the user has confirmed the SAS on the desktop. */
1654
1660
  e2eAuthenticated: boolean;
@@ -2090,19 +2096,21 @@ export interface SessionManagerAPI {
2090
2096
  };
2091
2097
  memory: {
2092
2098
  /** List markdown memory entries for the given workspace (defaults to 'default'). */
2093
- list: (workspace?: string) => Promise<MemoryListResult>;
2099
+ list: (target?: MemoryTarget) => Promise<MemoryListResult>;
2094
2100
  /** Read the contents of one memory entry. */
2095
- read: (name: string, workspace?: string) => Promise<MemoryReadResult>;
2101
+ read: (name: string, target?: MemoryTarget) => Promise<MemoryReadResult>;
2096
2102
  /** Atomic write of a memory entry (max 1 MiB). */
2097
- write: (name: string, content: string, workspace?: string) => Promise<MemoryMutationResult>;
2103
+ write: (name: string, content: string, target?: MemoryTarget) => Promise<MemoryMutationResult>;
2098
2104
  /** Delete a memory entry. */
2099
- delete: (name: string, workspace?: string) => Promise<MemoryMutationResult>;
2105
+ delete: (name: string, target?: MemoryTarget) => Promise<MemoryMutationResult>;
2100
2106
  /** Create a new memory entry with starter frontmatter + body. */
2101
- create: (name: string, description?: string, workspace?: string) => Promise<MemoryMutationResult>;
2107
+ create: (name: string, description?: string, target?: MemoryTarget) => Promise<MemoryMutationResult>;
2102
2108
  /** Aggregate workspace memories into semantic clusters. `refresh:true` fires a cost-gated `claude -p` pass; otherwise returns the cache only. */
2103
2109
  aggregate: (workspace: string, refresh?: boolean, cwd?: string) => Promise<MemoryAggregateResult>;
2104
2110
  /** Deterministic, zero-LLM-cost staleness report. `cwd` (optional) scopes the dead-repo-ref check. */
2105
- stale: (workspace?: string, cwd?: string) => Promise<MemoryStaleResult>;
2111
+ stale: (target?: MemoryTarget) => Promise<MemoryStaleResult>;
2112
+ /** Agents (user + project) with their `memory:` frontmatter scope. */
2113
+ agents: (cwd?: string) => Promise<{ agents: MemoryAgent[] }>;
2106
2114
  };
2107
2115
  projectBrief: {
2108
2116
  /** Read brief.json (or null if none yet) plus cheaply-computed source/drift metadata. Never fires an LLM call. */
@@ -2150,21 +2158,6 @@ export interface SessionManagerAPI {
2150
2158
  fields: { cwd: string; epicId: string; source: string },
2151
2159
  ) => Promise<{ ok: boolean }>;
2152
2160
  };
2153
- agentMemory: {
2154
- /** List all memory entries for one subagent. Sorted newest first. */
2155
- list: (agentId: string) => Promise<AgentMemoryListResult>;
2156
- /** Get one entry's full body. Returns `{entry:null}` if missing. */
2157
- get: (agentId: string, entryId: string) => Promise<AgentMemoryGetResult>;
2158
- /** Upsert one entry. Atomic write through config.cjs; max body 1 MiB. */
2159
- set: (
2160
- agentId: string,
2161
- entryId: string,
2162
- body: string,
2163
- category?: AgentMemoryCategory,
2164
- ) => Promise<AgentMemoryMutationResult>;
2165
- /** Delete one entry. Removes the file outright when last entry is removed. */
2166
- delete: (agentId: string, entryId: string) => Promise<AgentMemoryMutationResult>;
2167
- };
2168
2161
  git: {
2169
2162
  /** Full git status for `cwd`. Returns null when not a git repo, git is
2170
2163
  * missing, or the call times out (5s ceiling). Cached per-cwd for 5s. */
@@ -2260,6 +2253,8 @@ export interface SessionManagerAPI {
2260
2253
  }) => Promise<Exchange[]>;
2261
2254
  };
2262
2255
  promptSessions: {
2256
+ /** Main requests approve-and-start of a verified inbound proposal. */
2257
+ onInboundAutoApprove: (handler: (e: { cwd: string; epicId: string }) => void) => () => void;
2263
2258
  /** Fires when the main process appends an event to a PromptSession's
2264
2259
  * chain on disk (currently only the scheduler's response-event append
2265
2260
  * from notifyOriginatingTab) — lets an already-hydrated Epic pick it up
@@ -1,5 +1,15 @@
1
1
  const { contextBridge, ipcRenderer } = require('electron');
2
2
 
3
+ /** Forward only the defined addressing keys of a memory target. */
4
+ function memoryTarget(target) {
5
+ const out = {};
6
+ if (!target || typeof target !== 'object') return out;
7
+ if (target.workspace) out.workspace = target.workspace;
8
+ if (target.cwd) out.cwd = target.cwd;
9
+ if (target.agent) out.agent = target.agent;
10
+ return out;
11
+ }
12
+
3
13
  contextBridge.exposeInMainWorld('api', {
4
14
  app: {
5
15
  version: () => ipcRenderer.invoke('app:version'),
@@ -335,22 +345,20 @@ contextBridge.exposeInMainWorld('api', {
335
345
  writeText: (text) => ipcRenderer.invoke('clipboard:write-text', { text }),
336
346
  },
337
347
  memory: {
338
- list: (workspace) => ipcRenderer.invoke('memory:list', workspace ? { workspace } : {}),
339
- read: (name, workspace) => ipcRenderer.invoke('memory:read', workspace ? { name, workspace } : { name }),
340
- write: (name, content, workspace) =>
341
- ipcRenderer.invoke('memory:write', workspace ? { name, content, workspace } : { name, content }),
342
- delete: (name, workspace) =>
343
- ipcRenderer.invoke('memory:delete', workspace ? { name, workspace } : { name }),
344
- create: (name, description, workspace) => {
345
- const payload = { name };
348
+ list: (target) => ipcRenderer.invoke('memory:list', memoryTarget(target)),
349
+ read: (name, target) => ipcRenderer.invoke('memory:read', { name, ...memoryTarget(target) }),
350
+ write: (name, content, target) =>
351
+ ipcRenderer.invoke('memory:write', { name, content, ...memoryTarget(target) }),
352
+ delete: (name, target) => ipcRenderer.invoke('memory:delete', { name, ...memoryTarget(target) }),
353
+ create: (name, description, target) => {
354
+ const payload = { name, ...memoryTarget(target) };
346
355
  if (description) payload.description = description;
347
- if (workspace) payload.workspace = workspace;
348
356
  return ipcRenderer.invoke('memory:create', payload);
349
357
  },
350
358
  aggregate: (workspace, refresh, cwd) =>
351
359
  ipcRenderer.invoke('memory:aggregate', { workspace, ...(refresh ? { refresh: true } : {}), ...(cwd ? { cwd } : {}) }),
352
- stale: (workspace, cwd) =>
353
- ipcRenderer.invoke('memory:stale', { ...(workspace ? { workspace } : {}), ...(cwd ? { cwd } : {}) }),
360
+ stale: (target) => ipcRenderer.invoke('memory:stale', memoryTarget(target)),
361
+ agents: (cwd) => ipcRenderer.invoke('memory:agents', cwd ? { cwd } : {}),
354
362
  },
355
363
  projectBrief: {
356
364
  get: (cwd) => ipcRenderer.invoke('project-brief:get', { cwd }),
@@ -381,16 +389,6 @@ contextBridge.exposeInMainWorld('api', {
381
389
  auditLog: {
382
390
  append: (kind, fields) => ipcRenderer.invoke('auditLog:append', { kind, ...fields }),
383
391
  },
384
- agentMemory: {
385
- list: (agentId) => ipcRenderer.invoke('agent-memory:list', { agentId }),
386
- get: (agentId, entryId) => ipcRenderer.invoke('agent-memory:get', { agentId, entryId }),
387
- set: (agentId, entryId, body, category) => {
388
- const payload = { agentId, entryId, body };
389
- if (category) payload.category = category;
390
- return ipcRenderer.invoke('agent-memory:set', payload);
391
- },
392
- delete: (agentId, entryId) => ipcRenderer.invoke('agent-memory:delete', { agentId, entryId }),
393
- },
394
392
  git: {
395
393
  // Returns null when cwd is not a git repo, git is missing, or the call
396
394
  // times out (5s ceiling). The existing `app.gitBranch` is intentionally
@@ -543,6 +541,12 @@ contextBridge.exposeInMainWorld('api', {
543
541
  list: (payload) => ipcRenderer.invoke('exchanges:list', payload),
544
542
  },
545
543
  promptSessions: {
544
+ /** Main asks the renderer to approve-and-start a verified inbound proposal. */
545
+ onInboundAutoApprove: (handler) => {
546
+ const listener = (_e, payload) => handler(payload);
547
+ ipcRenderer.on('promptSessions:inbound-auto-approve', listener);
548
+ return () => ipcRenderer.removeListener('promptSessions:inbound-auto-approve', listener);
549
+ },
546
550
  /** Fires when the main process (currently only the scheduler's
547
551
  * response-event append) appends an event to a PromptSession's chain on
548
552
  * disk — lets an already-hydrated Epic pick it up live instead of
@@ -1,8 +0,0 @@
1
- import{b2 as I,aW as K,aE as i,aV as A,ao as e,n as F,Y as oe,ac as H,a4 as le,ag as V}from"./index-CTBpf-26.js";import{P as q}from"./Panel-_VZ_uXNg.js";import{L as ee}from"./ListDetail-PStOuQAo.js";import{M as te}from"./MarkdownEditor-DPw-N2-m.js";import{V as se}from"./ViewTabs-9lQCRwWJ.js";import"./monaco-editor-BsIlgG7c.js";function ie(t){return!t||!t.trim()?"default":t.replace(/[^a-zA-Z0-9]/g,"-")}const re=/^[a-z0-9-_]+$/;function ce(t,l,n,o){return t.filter(x=>!l||x.name.toLowerCase().includes(l.toLowerCase())).filter(x=>{var s;return!n||((s=o[x.name])==null?void 0:s.stale)===!0})}const de=["Commands:"," list — show all memory entries"," show <slug> — read one entry"," search <text> — find entries containing <text>"," forget <slug> — delete an entry (with confirm)"," remember <free-form> — ask Claude to save this"," help — this message","","Anything else: I will offer to send it to Claude in the terminal,","or add it as a new memory entry directly."].join(`
2
- `);function me(t){const l=t.trim();if(!l)return{kind:"freeform",arg:"",raw:t};const n=l.toLowerCase();if(n==="help"||n==="?")return{kind:"help",arg:"",raw:t};if(n==="list"||n==="ls"||n==="list memories"||n==="list all"||n==="what do you remember"||n==="what do you remember?")return{kind:"list",arg:"",raw:t};const o=l.match(/^(?:show|read|open)\s+(.+)$/i);if(o)return{kind:"show",arg:o[1].trim(),raw:t};const x=l.match(/^(?:forget|delete|remove)\s+(.+)$/i);if(x)return{kind:"forget",arg:x[1].trim(),raw:t};const s=l.match(/^(?:search|find)\s+(.+)$/i)||l.match(/^what (?:do )?you know about\s+(.+?)\??$/i);if(s)return{kind:"search",arg:s[1].trim(),raw:t};const f=l.match(/^remember(?:\s+that)?\s+(.+)$/i)||l.match(/^note(?:\s+that)?\s+(.+)$/i)||l.match(/^save(?:\s+that)?\s+(.+)$/i);return f?{kind:"remember",arg:f[1].trim(),raw:t}:{kind:"freeform",arg:l,raw:t}}function G(t,l){const n=t.toLowerCase().replace(/\.md$/,""),o=l.find(f=>f.name.replace(/\.md$/,"").toLowerCase()===n);if(o)return o;const x=l.filter(f=>f.name.replace(/\.md$/,"").toLowerCase().startsWith(n));if(x.length===1)return x[0];const s=l.filter(f=>f.name.replace(/\.md$/,"").toLowerCase().includes(n));return s.length===1?s[0]:null}function Z(t){const l=new Set(["the","a","an","is","are","to","of","in","for","and","or"]);return t.toLowerCase().replace(/[^a-z0-9\s-]/g," ").split(/\s+/).filter(x=>x&&!l.has(x)).slice(0,4).join("-").replace(/-+/g,"-").replace(/^-|-$/g,"")||`note-${Date.now().toString(36).slice(-4)}`}function Q(t,l){return[`Please save this to my workspace memory (workspace: ${t}).`,`Use the memory tool (or write a markdown file under ~/.claude/projects/${t}/memory/).`,"Pick a short kebab-case filename and add a one-line title at the top.","","Memory to save:",l].join(`
3
- `)}function X(){return`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function xe({workspace:t,entries:l,onChanged:n}){const o=I(),x=K(),s=(x==null?void 0:x.id)??null,[f,u]=i.useState(""),[d,b]=i.useState(()=>[{id:X(),role:"system",text:"Hi — I am the memory assistant for this workspace. Type `help` for what I understand, or just talk to me."}]),[C,g]=i.useState(!1),j=i.useRef(null),L=i.useRef(null);i.useEffect(()=>{const a=j.current;a&&(a.scrollTop=a.scrollHeight)},[d]);const c=i.useCallback(a=>{b(p=>[...p,{...a,id:X()}])},[]),$=i.useCallback(()=>{if(l.length===0){c({role:"result",text:"No memories saved for this workspace yet."});return}c({role:"result",text:`Found ${l.length} ${l.length===1?"memory":"memories"}:`,payload:{kind:"entries",entries:l}})},[l,c]),M=i.useCallback(async a=>{const p=G(a,l);if(!p){c({role:"result",text:`No memory matches "${a}". Try \`list\` to see what is here.`});return}const y=await window.api.memory.read(p.name,t);if(y.error){A.error(`Memory read failed: ${y.error}`),c({role:"result",text:`Read failed: ${y.error}`});return}c({role:"result",text:`${p.name}`,payload:{kind:"entry",entry:p,content:y.content}})},[l,t,c]),D=i.useCallback(async a=>{const p=a.toLowerCase(),y=l.filter(k=>k.name.toLowerCase().includes(p)),O=[];for(const k of l){if(y.includes(k))continue;const B=await window.api.memory.read(k.name,t);if(B.error)continue;const m=B.content.split(`
4
- `),h=m.findIndex(N=>N.toLowerCase().includes(p));h>=0&&O.push({entry:k,snippet:m[h].trim().slice(0,200)})}const r=[...y.map(k=>({entry:k,snippet:"(filename match)"})),...O];if(r.length===0){c({role:"result",text:`No matches for "${a}".`});return}c({role:"result",text:`Found ${r.length} ${r.length===1?"match":"matches"} for "${a}":`,payload:{kind:"search",query:a,hits:r}})},[l,t,c]),R=i.useCallback(async a=>{const p=G(a,l);if(!p){c({role:"result",text:`No memory matches "${a}". Try \`list\` to see what is here.`});return}if(!window.confirm(`Delete memory "${p.name}"? This cannot be undone.`))return;const y=await window.api.memory.delete(p.name,t);if(!y.ok){A.error(`Memory delete failed: ${y.error??"unknown error"}`),c({role:"result",text:`Delete failed: ${y.error??"unknown error"}`});return}c({role:"result",text:`Deleted ${p.name}.`}),n()},[l,t,c,n]),w=i.useCallback(a=>{const p=Z(a),y=Q(t,a);c({role:"result",text:"I do not write to memory from this panel. Send the request to Claude in the active terminal, or add it directly as a new memory entry below.",payload:{kind:"suggest-send",prompt:y,original:a}}),c({role:"result",text:`Add it directly as \`${p}.md\` instead?`,payload:{kind:"suggest-add",slug:p,content:a}})},[t,c]),_=i.useCallback(a=>{const p=Z(a),y=Q(t,a);c({role:"result",text:"Not a recognised command. Options:",payload:{kind:"suggest-send",prompt:y,original:a}}),c({role:"result",text:`…or save it directly as \`${p}.md\`.`,payload:{kind:"suggest-add",slug:p,content:a}})},[t,c]),E=i.useCallback(async a=>{g(!0);try{switch(a.kind){case"list":$();break;case"show":await M(a.arg);break;case"search":await D(a.arg);break;case"forget":await R(a.arg);break;case"help":c({role:"result",text:de});break;case"remember":w(a.arg);break;case"freeform":_(a.arg);break}}finally{g(!1)}},[$,M,D,R,w,_,c]),T=i.useCallback(async()=>{const a=f.trim();!a||C||(u(""),c({role:"user",text:a}),await E(me(a)))},[f,C,E,c]),v=a=>{a.key==="Enter"&&!a.shiftKey&&(a.preventDefault(),T())},S=i.useCallback(a=>{if(!s){A.error("No active terminal session — open or focus a tab first.");return}window.api.pty.write({tabId:s,data:a}),A.info("Prompt sent to active terminal (not submitted)")},[s]),P=i.useCallback(async(a,p)=>{if(!re.test(a)){A.error(`Invalid slug "${a}" — must be [a-z0-9-_]+`);return}const y=`${a}.md`,O=await window.api.memory.create(y,void 0,t);if(!O.ok){A.error(`Create failed: ${O.error??"unknown error"}`);return}const r=await window.api.memory.read(y,t),k=r.error?p:`${r.content.trimEnd()}
5
-
6
- ${p}
7
- `,B=await window.api.memory.write(y,k,t);if(!B.ok){A.error(`Write failed: ${B.error??"unknown error"}`);return}c({role:"result",text:`Saved as ${y}.`}),n()},[t,c,n]),W=i.useMemo(()=>[{label:"List memories",value:"list"},{label:"Help",value:"help"},{label:"Search…",value:"search "},{label:"Remember…",value:"remember "}],[]);return o?e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{ref:j,className:"flex-1 min-h-0 overflow-y-auto px-3 py-2 space-y-2",children:[d.map(a=>e.jsx(fe,{message:a,entries:l,onShow:p=>{var y;u(`show ${p.replace(/\.md$/,"")}`),(y=L.current)==null||y.focus()},onSendToClaude:S,onAddDirect:P},a.id)),C&&e.jsx("div",{className:"text-xs text-fg-faint italic",children:"working…"})]}),e.jsxs("div",{className:"shrink-0 border-t border-line px-3 py-2 space-y-2",children:[e.jsx("div",{className:"flex gap-1 flex-wrap",children:W.map(a=>e.jsx("button",{onClick:()=>{var p;u(a.value),(p=L.current)==null||p.focus()},className:"px-2 py-0.5 text-[11px] text-fg-faint hover:text-fg-dim border border-line hover:border-fg-faint rounded transition-colors",children:a.label},a.label))}),e.jsxs("div",{className:"flex gap-2 items-end",children:[e.jsx(oe,{}),e.jsx("textarea",{ref:L,value:f,onChange:a=>u(a.target.value),onKeyDown:v,placeholder:"Type `list`, `show <slug>`, `search <text>`, `remember …`, or talk freely. Enter to send, Shift+Enter for newline.",rows:2,className:"flex-1 bg-bg border border-line rounded px-2 py-1 text-xs text-fg resize-none focus:outline-none focus:border-fg-faint"}),e.jsx("button",{onClick:T,disabled:!f.trim()||C,className:"px-3 py-1 text-xs text-fg border border-line hover:bg-bg-hi rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:"Send"})]}),e.jsxs("div",{className:"text-[10px] text-fg-faint",children:["Workspace: ",e.jsx("span",{className:"font-mono",children:t}),!s&&e.jsx("span",{className:"ml-2 text-amber-400",children:"(no active terminal — Send-to-Claude disabled)"})]})]})]}):e.jsx(F,{title:"loading…"})}function fe({message:t,entries:l,onShow:n,onSendToClaude:o,onAddDirect:x}){var f,u,d,b,C;const s=t.role==="user"?"bg-bg-hi text-fg ml-8":t.role==="system"?"bg-bg text-fg-dim italic":"bg-bg text-fg-dim";return e.jsxs("div",{className:`text-xs rounded border border-line px-3 py-2 ${s}`,children:[e.jsx("div",{className:"whitespace-pre-wrap font-mono text-[11px] leading-relaxed",children:t.text}),((f=t.payload)==null?void 0:f.kind)==="entries"&&e.jsx("div",{className:"mt-2 space-y-0.5",children:t.payload.entries.map(g=>e.jsxs("button",{onClick:()=>n(g.name),className:"block w-full text-left px-2 py-0.5 text-[11px] text-fg-dim hover:text-fg hover:bg-bg-hi rounded font-mono",children:[g.name," ",e.jsxs("span",{className:"text-fg-faint",children:["(",H(g.bytes),")"]})]},g.name))}),((u=t.payload)==null?void 0:u.kind)==="entry"&&e.jsx("pre",{className:"mt-2 max-h-64 overflow-auto px-2 py-1 bg-bg-hi rounded text-[11px] whitespace-pre-wrap text-fg",children:t.payload.content}),((d=t.payload)==null?void 0:d.kind)==="search"&&e.jsx("div",{className:"mt-2 space-y-1",children:t.payload.hits.map((g,j)=>e.jsxs("button",{onClick:()=>n(g.entry.name),className:"block w-full text-left px-2 py-1 hover:bg-bg-hi rounded",children:[e.jsx("div",{className:"font-mono text-[11px] text-fg-dim",children:g.entry.name}),e.jsx("div",{className:"text-[10px] text-fg-faint truncate",children:g.snippet})]},`${g.entry.name}-${j}`))}),((b=t.payload)==null?void 0:b.kind)==="suggest-send"&&e.jsxs("div",{className:"mt-2 flex gap-2 items-center",children:[e.jsx("button",{onClick:()=>o(t.payload.kind==="suggest-send"?t.payload.prompt:""),className:"px-2 py-0.5 text-[11px] text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:"Send to Claude in terminal"}),e.jsx("span",{className:"text-[10px] text-fg-faint",children:"paste-only — you press Enter"})]}),((C=t.payload)==null?void 0:C.kind)==="suggest-add"&&e.jsxs("div",{className:"mt-2 flex gap-2 items-center",children:[e.jsx("button",{onClick:()=>{var g;return((g=t.payload)==null?void 0:g.kind)==="suggest-add"&&x(t.payload.slug,t.payload.content)},className:"px-2 py-0.5 text-[11px] text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:"Add as new memory"}),e.jsxs("span",{className:"text-[10px] text-fg-faint font-mono",children:[t.payload.slug,".md"]})]})]})}const z=le((t,l)=>({entriesByAgent:new Map,loading:null,errorByAgent:new Map,loadAgent:async n=>{t({loading:n});const o=await window.api.agentMemory.list(n),x=new Map(l().entriesByAgent);x.set(n,o.entries);const s=new Map(l().errorByAgent);o.error?(s.set(n,o.error),A.error(`Agent memory load failed: ${o.error}`)):s.delete(n),t({entriesByAgent:x,loading:null,errorByAgent:s})},setEntry:async(n,o,x,s)=>{const f=await window.api.agentMemory.set(n,o,x,s);return f.ok&&await l().loadAgent(n),f},deleteEntry:async(n,o)=>{const x=await window.api.agentMemory.delete(n,o);if(x.ok){const s=new Map(l().entriesByAgent),f=s.get(n)??[];s.set(n,f.filter(u=>u.id!==o)),t({entriesByAgent:s})}return x}})),Y=/^[A-Za-z0-9._-]{1,128}$/,U={command:"Commands",preference:"Preferences",pattern:"Patterns",failure:"Avoid",workflow:"Workflow"},ne=["command","preference","pattern","failure","workflow"];async function ue(t,l){const n=[{dir:`${t}/.claude/agents`,scope:"user"}];l&&n.push({dir:`${l}/.claude/agents`,scope:"project"});const o=[];for(const{dir:s,scope:f}of n)try{const u=await window.api.config.listDir(s,{filesOnly:!0});for(const d of u.entries){if(!d.name.endsWith(".md"))continue;const b=d.name.replace(/\.md$/,"");Y.test(b)&&o.push({id:b,label:b,scope:f})}}catch{}const x=new Map;for(const s of o)s.scope==="project"?x.set(s.id,s):x.has(s.id)||x.set(s.id,s);return Array.from(x.values()).sort((s,f)=>s.id.localeCompare(f.id))}function he(){const t=I(),l=K(),n=(l==null?void 0:l.cwd)??null,[o,x]=i.useState([]),[s,f]=i.useState(null),[u,d]=i.useState(null),[b,C]=i.useState(""),[g,j]=i.useState(!1),[L,c]=i.useState(!1),[$,M]=i.useState({}),D=z(r=>r.entriesByAgent),R=z(r=>r.loadAgent),w=z(r=>r.setEntry),_=z(r=>r.deleteEntry);i.useEffect(()=>{if(!t)return;let r=!1;return ue(t,n).then(k=>{r||(x(k),f(B=>{var m;return B&&k.find(h=>h.id===B)?B:((m=k[0])==null?void 0:m.id)??null}))}),()=>{r=!0}},[t,n]),i.useEffect(()=>{s&&(d(null),R(s))},[s,R]);const E=s?D.get(s)??[]:[],T=i.useMemo(()=>E.filter(r=>!b||r.id.toLowerCase().includes(b.toLowerCase())||r.body.toLowerCase().includes(b.toLowerCase())),[E,b]),v=i.useMemo(()=>E.find(r=>r.id===u)??null,[E,u]);i.useEffect(()=>{var r;!u&&T.length>0?d(T[0].id):u&&!T.find(k=>k.id===u)&&d(((r=T[0])==null?void 0:r.id)??null)},[T,u]);const S=s&&u?`${s}::${u}`:null,P=S?$[S]??(v==null?void 0:v.body)??"":"",W=!!S&&v!==null&&P!==v.body,a=i.useCallback(async()=>{s&&await R(s)},[s,R]),p=async()=>{if(!s||!u||!S)return;j(!0);const r=await w(s,u,P,(v==null?void 0:v.category)??void 0);if(j(!1),!r.ok){A.error(`Save failed: ${r.error??"unknown"}`);return}M(k=>{const B={...k};return delete B[S],B})},y=async()=>{if(!s||!u||!window.confirm(`Delete memory "${u}"? This cannot be undone.`))return;j(!0);const r=await _(s,u);if(j(!1),!r.ok){A.error(`Delete failed: ${r.error??"unknown"}`);return}d(null)},O=async r=>{if(!s||!u||!v)return;j(!0);const k=await w(s,u,v.body,r??void 0);j(!1),k.ok||A.error(`Update failed: ${k.error??"unknown"}`)};return t?e.jsx(q,{toolbar:e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-fg-faint",children:"Agent"}),e.jsxs("select",{value:s??"",onChange:r=>f(r.target.value||null),className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg max-w-[16rem]",children:[o.length===0&&e.jsx("option",{value:"",children:"(no agents found)"}),o.map(r=>e.jsxs("option",{value:r.id,children:[r.label," ",r.scope==="project"?"· project":""]},`${r.scope}:${r.id}`))]}),e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsxs("span",{className:"text-fg-faint",children:[E.length," ",E.length===1?"entry":"entries"]}),e.jsx("div",{className:"flex-1"}),e.jsx("button",{onClick:()=>c(!0),disabled:!s,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40",children:"+ New entry"}),e.jsx("button",{onClick:a,disabled:!s,className:"ml-2 px-2 py-0.5 text-xs text-fg-faint hover:text-fg-dim border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40",children:"Refresh"})]}),footer:v?e.jsxs("div",{className:"px-3 py-1.5 flex items-center gap-3 text-xs",children:[e.jsx("button",{onClick:p,disabled:!W||g,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:g?"Saving…":"Save"}),e.jsx("button",{onClick:y,disabled:g,className:"px-2 py-0.5 text-xs text-red-400 hover:text-red-300 border border-line hover:border-red-400/50 rounded transition-colors disabled:opacity-40",children:"Delete"}),e.jsx("span",{className:"text-fg-faint",children:"Category:"}),e.jsxs("select",{value:v.category??"",onChange:r=>O(r.target.value||null),disabled:g,className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg-dim disabled:opacity-40",children:[e.jsx("option",{value:"",children:"(none)"}),ne.map(r=>e.jsx("option",{value:r,children:U[r]},r))]}),e.jsx("span",{className:"text-fg-faint truncate flex-1",children:v.id}),W&&e.jsx("span",{className:"text-accent",children:"unsaved"})]}):null,children:e.jsx(ee,{sidebar:e.jsxs("div",{className:"py-1",children:[e.jsx("div",{className:"px-2 pb-1",children:e.jsx("input",{value:b,onChange:r=>C(r.target.value),placeholder:"filter…",className:"w-full bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg"})}),s?T.length===0?e.jsx("div",{className:"px-3 py-2 text-xs text-fg-faint italic",children:E.length===0?"no entries yet":"no matches"}):T.map(r=>e.jsxs("button",{onClick:()=>d(r.id),className:`w-full text-left px-3 py-1 text-xs flex items-center justify-between ${u===r.id?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,title:r.id,children:[e.jsx("span",{className:"truncate",children:r.id}),e.jsxs("span",{className:"ml-2 text-fg-faint shrink-0 text-[10px]",children:[H(r.bytes)," · ",V(r.updatedAt)]})]},r.id)):e.jsx("div",{className:"px-3 py-2 text-xs text-fg-faint italic",children:"select an agent first"})]}),detail:L&&s?e.jsx(pe,{agentId:s,existingIds:new Set(E.map(r=>r.id)),onCancel:()=>c(!1),onCreated:r=>{c(!1),d(r),R(s)}}):v&&s&&u&&S?e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 px-3 py-2 border-b border-line text-xs",children:[e.jsx("div",{className:"text-fg truncate",children:v.id}),e.jsxs("div",{className:"text-fg-faint truncate",children:[H(v.bytes)," · modified ",V(v.updatedAt),v.category?` · ${U[v.category]}`:""]})]}),e.jsx("div",{className:"flex-1 min-h-0",children:e.jsx(te,{path:`agent-memory/${s}/${v.id}.md`,value:P,onChange:r=>M(k=>({...k,[S]:r}))})})]}):e.jsx(F,{title:s?E.length===0?"no entries yet":"select an entry":"no agent selected",hint:s&&E.length===0?"Use “+ New entry” to give this subagent its first memory.":void 0})})}):e.jsx(F,{title:"loading…"})}function pe({agentId:t,existingIds:l,onCancel:n,onCreated:o}){const x=z(w=>w.setEntry),[s,f]=i.useState(""),[u,d]=i.useState(""),[b,C]=i.useState(""),[g,j]=i.useState(!1),[L,c]=i.useState(null),$=s.trim(),M=Y.test($)&&!l.has($),D=s?Y.test($)?l.has($)?"an entry with this id already exists":null:"must match [A-Za-z0-9._-]+":null,R=async()=>{if(!M)return;j(!0),c(null);const w=await x(t,$,u,b||void 0);if(j(!1),!w.ok){c(w.error??"create failed");return}o($)};return e.jsxs("div",{className:"p-4 max-w-xl space-y-3",children:[e.jsxs("div",{className:"text-sm text-fg",children:["New memory entry for ",e.jsx("span",{className:"font-mono text-fg-dim",children:t})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Entry id"}),e.jsx("input",{autoFocus:!0,value:s,onChange:w=>f(w.target.value),placeholder:"e.g. preferred-test-runner",className:"w-full bg-bg border border-line rounded px-2 py-1 text-xs text-fg font-mono"}),D&&e.jsx("div",{className:"text-[10px] text-red-400",children:D}),e.jsxs("div",{className:"text-[10px] text-fg-faint",children:["Letters, digits, dots, dashes, underscores. Persists at",e.jsxs("code",{className:"ml-1 font-mono text-fg-dim",children:["~/.claude/session-manager/agent-memory/",t,".json"]}),"."]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Category (optional)"}),e.jsxs("select",{value:b,onChange:w=>C(w.target.value),className:"bg-bg border border-line rounded px-2 py-1 text-xs text-fg",children:[e.jsx("option",{value:"",children:"(none)"}),ne.map(w=>e.jsx("option",{value:w,children:U[w]},w))]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Body (markdown)"}),e.jsx("textarea",{value:u,onChange:w=>d(w.target.value),rows:6,placeholder:"What should this subagent remember?",className:"w-full bg-bg border border-line rounded px-2 py-1 text-xs text-fg font-mono"})]}),L&&e.jsx("div",{className:"text-xs text-red-400",children:L}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:R,disabled:!M||g,className:"px-3 py-1 text-xs text-fg border border-line hover:bg-bg-hi rounded transition-colors disabled:opacity-40",children:g?"Creating…":"Create"}),e.jsx("button",{onClick:n,disabled:g,className:"px-3 py-1 text-xs text-fg-faint hover:text-fg-dim rounded transition-colors",children:"Cancel"})]})]})}function ge({workspace:t,cwd:l,onOpenMember:n}){const[o,x]=i.useState(null),[s,f]=i.useState(!1),[u,d]=i.useState(!1),b=i.useRef(0),C=i.useCallback(async c=>{const $=++b.current;f(!0);try{const M=await window.api.memory.aggregate(t,c,l??void 0);if(b.current!==$)return;x(M)}catch(M){if(b.current!==$)return;A.error(`Memory aggregate failed: ${M instanceof Error?M.message:String(M)}`)}finally{b.current===$&&(f(!1),d(!0))}},[t,l]);i.useEffect(()=>{x(null),d(!1),C(!1)},[C]);const g=(o==null?void 0:o.clusters)??[],j=(o==null?void 0:o.orphans)??[],L=g.length>0||j.length>0;return e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 flex items-center gap-2 px-3 py-2 border-b border-line text-xs",children:[e.jsx("span",{className:"text-fg-faint",children:o!=null&&o.generatedAt?`generated ${V(o.generatedAt)} ago`:"not yet aggregated"}),e.jsx("div",{className:"flex-1"}),e.jsx("button",{onClick:()=>C(!0),disabled:s,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:s?"Aggregating…":o!=null&&o.generatedAt?"Refresh":"Aggregate"})]}),e.jsx("div",{className:"flex-1 min-h-0 overflow-auto p-3",children:u?L?e.jsxs("div",{className:"space-y-4",children:[g.length>0&&e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3",children:g.map(c=>e.jsx(be,{cluster:c,onOpenMember:n},c.id))}),j.length>0&&e.jsxs("div",{className:"border border-line rounded p-3",children:[e.jsx("div",{className:"text-xs text-fg mb-2",children:"Unclustered"}),e.jsx("div",{className:"flex flex-wrap gap-1",children:j.map(c=>e.jsx("button",{onClick:()=>n(c),className:"px-2 py-0.5 text-[11px] font-mono text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:c},c))})]})]}):e.jsx(F,{title:"no clusters yet",hint:"Click Aggregate to organize this workspace's memories into named clusters."}):e.jsx(F,{title:"loading…"})})]})}function be({cluster:t,onOpenMember:l}){return e.jsxs("div",{className:"border border-line rounded p-3",children:[e.jsx("div",{className:"text-sm text-fg",children:t.name}),t.summary&&e.jsx("div",{className:"text-xs text-fg-faint mt-0.5",children:t.summary}),e.jsx("div",{className:"mt-2 flex flex-wrap gap-1",children:t.memberSlugs.map(n=>e.jsx("button",{onClick:()=>l(n),className:"px-2 py-0.5 text-[11px] font-mono text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:n},n))}),t.links.length>0&&e.jsx("div",{className:"mt-2 space-y-0.5",children:t.links.map((n,o)=>e.jsxs("div",{className:"text-[10px] text-fg-faint font-mono",children:[n.from," → ",n.to,n.label?` (${n.label})`:""]},`${n.from}-${n.to}-${o}`))})]})}const ye=[{key:"workspace",label:"Workspace"},{key:"subagent",label:"Subagent"}],J="sm.memoryTab.scope";function je(){const[t,l]=i.useState(()=>localStorage.getItem(J)==="subagent"?"subagent":"workspace");return i.useEffect(()=>{localStorage.setItem(J,t)},[t]),e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 flex items-center gap-3 px-3 py-2 border-b border-line",children:[e.jsx(se,{options:ye,active:t,onChange:l}),e.jsx("span",{className:"text-[11px] text-fg-faint",children:t==="workspace"?"memories scoped to the active project":"long-term memory per subagent"})]}),e.jsx("div",{className:"flex-1 min-h-0",children:t==="workspace"?e.jsx(ve,{}):e.jsx(he,{})})]})}const Me=i.memo(je);function ve(){const t=I(),l=K(),n=(l==null?void 0:l.cwd)??null,o=i.useMemo(()=>ie(n),[n]),[x,s]=i.useState("classic"),[f,u]=i.useState([]),[d,b]=i.useState(null),[C,g]=i.useState(""),[j,L]=i.useState({}),[c,$]=i.useState(!1),[M,D]=i.useState({}),[R,w]=i.useState({}),[_,E]=i.useState(!1),[T,v]=i.useState(!1),S=i.useCallback(async()=>{if(!t)return;const m=await window.api.memory.list(o);u(m.entries),m.error&&A.error(`Memory list failed: ${m.error}`),b(h=>{var N;return h&&m.entries.some(ae=>ae.name===h)?h:((N=m.entries[0])==null?void 0:N.name)??null});try{const h=await window.api.memory.stale(o,n??void 0);L(Object.fromEntries(h.entries.map(N=>[N.name,N])))}catch(h){L({}),A.error(`Memory staleness check failed: ${h instanceof Error?h.message:String(h)}`)}},[t,o,n]);if(i.useEffect(()=>{D({}),w({}),b(null),S()},[o,S]),i.useEffect(()=>{if(!t)return;const m=`${t}/.claude/projects/${o}/memory`;window.api.config.watch([m]);const h=window.api.config.onChanged(({path:N})=>{(N===m||N.startsWith(m+"/"))&&S()});return()=>{h(),window.api.config.unwatch([m])}},[t,o,S]),i.useEffect(()=>{if(!d||M[d]!==void 0)return;let m=!1;return(async()=>{const h=await window.api.memory.read(d,o);if(!m){if(h.error){A.error(`Memory read failed: ${h.error}`);return}D(N=>({...N,[d]:h.content})),w(N=>({...N,[d]:h.content}))}})(),()=>{m=!0}},[d,o]),!t)return e.jsx(F,{title:"loading…"});const P=f.reduce((m,h)=>{var N;return m+((N=j[h.name])!=null&&N.stale?1:0)},0),W=ce(f,C,c,j),a=f.find(m=>m.name===d)??null,p=d?j[d]:void 0,y=d?M[d]??"":"",O=d?R[d]??"":"",r=d?y!==O:!1,k=async()=>{if(!d)return;E(!0);const m=await window.api.memory.write(d,y,o);if(E(!1),!m.ok){A.error(`Memory save failed: ${m.error??"unknown error"}`);return}w(h=>({...h,[d]:y})),S()},B=async()=>{if(!d||!window.confirm(`Delete memory "${d}"? This cannot be undone.`))return;E(!0);const m=await window.api.memory.delete(d,o);if(E(!1),!m.ok){A.error(`Memory delete failed: ${m.error??"unknown error"}`);return}D(h=>{const N={...h};return delete N[d],N}),w(h=>{const N={...h};return delete N[d],N}),b(null),S()};return e.jsx(q,{toolbar:e.jsxs(e.Fragment,{children:[e.jsx(se,{options:[{key:"classic",label:"Editor"},{key:"natural",label:"Natural"},{key:"clusters",label:"Clusters"}],active:x,onChange:s}),e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsx("span",{className:"text-fg-faint",children:"Workspace"}),e.jsx("span",{className:"font-mono text-fg-dim truncate max-w-[24rem]",title:o,children:o}),e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsxs("span",{className:"text-fg-faint",children:[f.length," ",f.length===1?"entry":"entries"]}),e.jsx("div",{className:"flex-1"}),x==="classic"&&P>0&&e.jsxs("button",{onClick:()=>$(m=>!m),className:`px-2 py-0.5 text-xs border rounded transition-colors ${c?"text-amber-400 border-amber-400/50 bg-amber-400/10":"text-fg-dim hover:text-fg border-line hover:border-fg-faint"}`,children:["Stale only (",P,")"]}),x==="classic"&&e.jsx("button",{onClick:()=>v(!0),className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:"+ New memory"}),x!=="clusters"&&e.jsx("button",{onClick:S,className:"ml-2 px-2 py-0.5 text-xs text-fg-faint hover:text-fg-dim border border-line hover:border-fg-faint rounded transition-colors",children:"Refresh"})]}),footer:x==="classic"&&a?e.jsxs("div",{className:"px-3 py-1.5 flex items-center gap-3 text-xs",children:[e.jsx("button",{onClick:k,disabled:!r||_,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:_?"Saving…":"Save"}),e.jsx("button",{onClick:B,disabled:_,className:"px-2 py-0.5 text-xs text-red-400 hover:text-red-300 border border-line hover:border-red-400/50 rounded transition-colors disabled:opacity-40",children:"Delete"}),(p==null?void 0:p.stale)&&e.jsxs("span",{className:"text-amber-400/90 truncate",title:p.reasons.join("; "),children:["stale · ",p.reasons.join(", ")]}),e.jsx("span",{className:"text-fg-faint truncate flex-1",children:a.path}),r&&e.jsx("span",{className:"text-accent",children:"unsaved"})]}):null,children:x==="natural"?e.jsx(xe,{workspace:o,entries:f,onChanged:S}):x==="clusters"?e.jsx(ge,{workspace:o,cwd:n,onOpenMember:m=>{b(`${m}.md`),s("classic")}}):e.jsx(ee,{sidebar:e.jsxs("div",{className:"py-1",children:[e.jsx("div",{className:"px-2 pb-1",children:e.jsx("input",{value:C,onChange:m=>g(m.target.value),placeholder:"filter…",className:"w-full bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg"})}),W.length===0?e.jsx("div",{className:"px-3 py-2 text-xs text-fg-faint italic",children:f.length===0?"no memories yet":"no matches"}):W.map(m=>{const h=j[m.name],N=h!=null&&h.stale?`${m.path}
8
- stale: ${h.reasons.join(", ")}`:m.path;return e.jsxs("button",{onClick:()=>b(m.name),className:`w-full text-left px-3 py-1 text-xs flex items-center justify-between ${d===m.name?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,title:N,children:[e.jsxs("span",{className:"truncate flex items-center",children:[e.jsx("span",{className:"truncate",children:m.name.replace(/\.md$/,"")}),(h==null?void 0:h.stale)&&e.jsx("span",{className:"ml-1.5 shrink-0 px-1 rounded text-[9px] uppercase tracking-wide text-amber-400/90 border border-amber-400/30",children:"stale"})]}),e.jsxs("span",{className:"ml-2 text-fg-faint shrink-0 text-[10px]",children:[H(m.bytes)," · ",V(m.mtimeMs)]})]},m.name)})]}),detail:T?e.jsx(we,{workspace:o,onCancel:()=>v(!1),onCreated:m=>{v(!1),b(m),S()}}):a&&d?e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 px-3 py-2 border-b border-line text-xs",children:[e.jsx("div",{className:"text-fg truncate",children:a.name}),e.jsxs("div",{className:"text-fg-faint truncate",children:[H(a.bytes)," · modified ",V(a.mtimeMs)]})]}),e.jsx("div",{className:"flex-1 min-h-0",children:e.jsx(te,{path:a.path,value:y,onChange:m=>D(h=>({...h,[d]:m}))})})]}):e.jsx(F,{title:f.length===0?"no memories yet":"select a memory",hint:f.length===0?"Claude writes to memories during sessions when you ask it to remember things, or you can create one manually with the + New memory button.":void 0})})})}function we({workspace:t,onCancel:l,onCreated:n}){const[o,x]=i.useState(""),[s,f]=i.useState(""),[u,d]=i.useState(!1),[b,C]=i.useState(null),g=o.trim().toLowerCase(),j=re.test(g),L=async()=>{if(!j){C("Slug must match [a-z0-9-_]+");return}d(!0),C(null);const c=`${g}.md`,$=await window.api.memory.create(c,s.trim()||void 0,t);if(d(!1),!$.ok){C($.error??"create failed");return}n(c)};return e.jsxs("div",{className:"p-4 max-w-xl space-y-3",children:[e.jsx("div",{className:"text-sm text-fg",children:"New memory"}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Slug"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{autoFocus:!0,value:o,onChange:c=>x(c.target.value),placeholder:"my-notes",className:"flex-1 bg-bg border border-line rounded px-2 py-1 text-xs text-fg font-mono"}),e.jsx("span",{className:"text-xs text-fg-faint",children:".md"})]}),e.jsxs("div",{className:"text-[10px] text-fg-faint",children:["Lowercase letters, digits, dashes, underscores. The file is created at",e.jsxs("code",{className:"ml-1 font-mono text-fg-dim",children:["~/.claude/projects/",t,"/memory/",g||"…",".md"]})]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Description (optional)"}),e.jsx("input",{value:s,onChange:c=>f(c.target.value),placeholder:"one-line summary",className:"w-full bg-bg border border-line rounded px-2 py-1 text-xs text-fg"})]}),b&&e.jsx("div",{className:"text-xs text-red-400",children:b}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:L,disabled:!j||u,className:"px-3 py-1 text-xs text-fg border border-line hover:bg-bg-hi rounded transition-colors disabled:opacity-40",children:u?"Creating…":"Create"}),e.jsx("button",{onClick:l,disabled:u,className:"px-3 py-1 text-xs text-fg-faint hover:text-fg-dim rounded transition-colors",children:"Cancel"})]})]})}export{Me as Memory};
@@ -1 +0,0 @@
1
- import{a4 as P,b2 as G,aE as x,ao as i,Z as R,B as T,a as _}from"./index-CTBpf-26.js";import{a as E,C as B,c as L,d as S,b as I}from"./Library-DPE1h-1L.js";const O=e=>`${e}/.claude/session-manager/provenance.json`;function D(e){if(!e||typeof e!="object")return{};const t={};for(const[n,o]of Object.entries(e))(o==="anthropic"||o==="community"||o==="local")&&(t[n]=o);return t}const K=P((e,t)=>({overrides:{},loaded:!1,loading:!1,home:null,load:async n=>{const o=t();if(!(o.home===n&&(o.loaded||o.loading))){e({home:n,loading:!0});try{const r=await window.api.config.readJson(O(n)),d=r.exists?D(r.data):{};e(h=>({overrides:{...d,...h.overrides},loaded:!0,loading:!1}))}catch{e({loaded:!0,loading:!1})}}},setOverride:async(n,o)=>{const r={...t().overrides};o===null?delete r[n]:r[n]=o,e({overrides:r});const d=t().home;if(d)try{await window.api.config.writeJson(O(d),r)}catch{}}})),a=e=>(e??"").toLowerCase().trim(),j=new Set,$=new Set;for(const e of I){const t=e.official?j:$;t.add(a(e.id)),t.add(a(e.name))}const H=new Set(S.filter(e=>e.official).flatMap(e=>[a(e.id),a(e.name)])),J=new Set(S.filter(e=>!e.official).flatMap(e=>[a(e.id),a(e.name)])),U=new Set(L.filter(e=>e.official).flatMap(e=>[a(e.id),a(e.name)])),V=new Set(L.filter(e=>!e.official).flatMap(e=>[a(e.id),a(e.name)])),z=new Set(B.flatMap(e=>[a(e.id),a(e.name)])),Z=new Set(E.flatMap(e=>[a(e.id),a(e.name)])),q=new Set(E.map(e=>e.command.trim())),F=new Set(["security-review","pr-comments","release-notes","add-dir","code-review","simplify"]),Q=/(@anthropic-ai\/|anthropic\.com|github\.com\/anthropics)/i,W=/(^|\s)(npx|uvx|pipx|bunx|mcp-remote|deno\s+run|docker\s+run)(\s|$)/i;function X(e){return[e.command,...e.args??[],e.url,e.repository,e.homepage].filter(Boolean).join(" ").toLowerCase()}const l=e=>({provenance:"anthropic",reason:e}),y=e=>({provenance:"community",reason:e});function Y(e,t,n){switch(e){case"mcp":return j.has(t)?l("official MCP server (catalog)"):$.has(t)?y("third-party server (catalog)"):null;case"skill":case"command":return H.has(t)?l("official Anthropic skill (catalog)"):J.has(t)?y("third-party skill (catalog)"):null;case"plugin":return U.has(t)?l("official Anthropic plugin (catalog)"):V.has(t)?y("third-party plugin (catalog)"):null;case"subagent":return z.has(t)?l("bundled Anthropic subagent (catalog)"):null;case"hook":return Z.has(t)||n&&q.has(n.trim())?l("bundled hook (catalog)"):null}}function ee(e,t){return e.repository?"has a source repository":e.homepage?"has a homepage":e.url?"remote endpoint":W.test(t)?"installed from a package registry":null}function te(e){const t=a(e.name),n=X(e),o=Y(e.type,t,e.command);if(o)return o;if((e.type==="command"||e.type==="skill")&&F.has(t))return l("built-in Claude Code command");if(Q.test(n))return l("Anthropic package or repo");const r=ee(e,n);return r?y(r):{provenance:"local",reason:"no external source — assumed self-created"}}function ne(e,t,n){return`${e}:${t??"-"}:${n}`}const c={anthropic:"Anthropic",community:"Community",local:"Local"},N={anthropic:"accent",community:"warn",local:"dim"},oe=["anthropic","community","local"];function se({input:e,scope:t,interactive:n=!0,className:o=""}){const r=G(),{overrides:d,load:h,setOverride:k}=K(),[b,m]=x.useState(!1),v=x.useRef(null);x.useEffect(()=>{r&&h(r)},[r,h]),x.useEffect(()=>{if(!b)return;const s=C=>{v.current&&!v.current.contains(C.target)&&m(!1)},g=C=>{C.key==="Escape"&&m(!1)};return window.addEventListener("mousedown",s),window.addEventListener("keydown",g),()=>{window.removeEventListener("mousedown",s),window.removeEventListener("keydown",g)}},[b]);const w=ne(e.type,t??null,e.name),f=te(e),p=d[w],u=p??f.provenance,M=p?`Origin manually set to ${c[u]} — auto-detected ${c[f.provenance]} (${f.reason})`:`Auto-detected ${c[u]}: ${f.reason}`,A=i.jsxs(_,{tone:N[u],title:M,className:`${n?"cursor-pointer hover:brightness-125":""} ${o}`,children:[c[u],p&&i.jsx("span",{className:"w-1 h-1 rounded-full bg-current opacity-70","aria-hidden":!0})]});return n?i.jsxs("span",{ref:v,className:"relative inline-flex",children:[i.jsx("button",{type:"button",onClick:s=>{s.stopPropagation(),m(g=>!g)},className:"inline-flex","aria-label":`Origin: ${c[u]}. Click to change.`,children:A}),b&&i.jsxs("span",{className:`absolute ${R.dialog} top-full right-0 mt-1 min-w-[7.5rem] rounded-md border border-line bg-bg-elev shadow-lg py-1 text-left`,onClick:s=>s.stopPropagation(),children:[i.jsx("span",{className:"block px-2 pb-1 text-[10px] uppercase tracking-wide text-fg-faint",children:"Mark origin as"}),oe.map(s=>i.jsxs("button",{type:"button",onClick:()=>{k(w,s),m(!1)},className:`flex w-full items-center gap-2 px-2 py-1 text-[12px] hover:bg-bg-hi/60 ${u===s?"text-fg":"text-fg-dim"}`,children:[i.jsx("span",{className:`w-2 h-2 rounded-full border ${T[N[s]]}`,"aria-hidden":!0}),c[s],p===s&&i.jsx("span",{className:"ml-auto text-[10px] text-fg-faint",children:"set"})]},s)),i.jsx("div",{className:"my-1 border-t border-line"}),i.jsxs("button",{type:"button",onClick:()=>{k(w,null),m(!1)},disabled:!p,className:"flex w-full items-center gap-2 px-2 py-1 text-[12px] text-fg-dim hover:bg-bg-hi/60 disabled:opacity-40 disabled:hover:bg-transparent",title:`Revert to auto-detection (${c[f.provenance]})`,children:["Auto-detect",i.jsx("span",{className:"ml-auto text-[10px] text-fg-faint",children:c[f.provenance]})]})]})]}):A}export{se as P};