@ivotoby/postgram-cli 1.30.0 → 1.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -49,6 +49,12 @@ export function createPgmClient(options) {
49
49
  body: input
50
50
  });
51
51
  },
52
+ groomSessionContext(input) {
53
+ return request(options, '/api/memory/session-context/groom', {
54
+ method: 'POST',
55
+ body: input
56
+ });
57
+ },
52
58
  recallEntity(id, input = {}) {
53
59
  const params = new URLSearchParams();
54
60
  if (input.owner) {
package/dist/pgm.js CHANGED
@@ -64,6 +64,43 @@ function validateToonOptions(options, json) {
64
64
  throw new AppError(ErrorCode.VALIDATION, '--toon cannot be combined with --json or --full-response');
65
65
  }
66
66
  }
67
+ function parseDurationMs(value) {
68
+ const match = /^(\d+)([mhd])$/i.exec(value.trim());
69
+ if (!match) {
70
+ throw new AppError(ErrorCode.VALIDATION, `Invalid duration '${value}'. Use format like '15m', '2h', or '7d'.`);
71
+ }
72
+ const [, amountText, unitText] = match;
73
+ const amount = Number(amountText);
74
+ const unit = unitText.toLowerCase();
75
+ const milliseconds = unit === 'm'
76
+ ? amount * 60 * 1000
77
+ : unit === 'h'
78
+ ? amount * 60 * 60 * 1000
79
+ : amount * 24 * 60 * 60 * 1000;
80
+ if (milliseconds > 3650 * 24 * 60 * 60 * 1000) {
81
+ throw new AppError(ErrorCode.VALIDATION, `Duration '${value}' exceeds the maximum allowed (3650 days / ~10 years).`);
82
+ }
83
+ return milliseconds;
84
+ }
85
+ function formatGroomCandidates(candidates) {
86
+ if (candidates.length === 0) {
87
+ return ['No eligible session-context memories'];
88
+ }
89
+ const lines = [`Would archive ${candidates.length} session-context memories`];
90
+ for (const candidate of candidates) {
91
+ const metadata = candidate.metadata;
92
+ lines.push([
93
+ ` ${shortId(candidate.id)} ${candidate.createdAt.slice(0, 10)}`,
94
+ candidate.tags.length ? `tags=${candidate.tags.join(',')}` : 'tags=-',
95
+ metadata.topic ? `topic=${metadata.topic}` : undefined,
96
+ metadata.session_id ? `session=${metadata.session_id}` : undefined,
97
+ candidate.content ? `content=${candidate.content}` : 'content=-'
98
+ ]
99
+ .filter(Boolean)
100
+ .join(' | '));
101
+ }
102
+ return lines;
103
+ }
67
104
  async function resolveStoreContent(content) {
68
105
  if (content !== undefined) {
69
106
  return content;
@@ -259,7 +296,6 @@ program
259
296
  .option('--expand-graph', 'include graph-connected entities in results')
260
297
  .option('--include-archived', 'include archived entities in results')
261
298
  .option('--memory-role <role>', 'memory role filter: durable_memory or session_context')
262
- .option('--include-other-clients-session-context', 'include session context from other clients')
263
299
  .option('--full-response', 'emit the full API response instead of compact default output when used with --json')
264
300
  .option('--toon', 'emit compact TOON output for lower agent token use; formatting is applied in the CLI, not the API')
265
301
  .action(async (query, options, command) => {
@@ -282,8 +318,7 @@ program
282
318
  recency_weight: Number(options.recencyWeight),
283
319
  expand_graph: options.expandGraph === true ? true : undefined,
284
320
  include_archived: options.includeArchived === true ? true : undefined,
285
- memory_role: options.memoryRole,
286
- include_other_clients_session_context: options.includeOtherClientsSessionContext === true ? true : undefined
321
+ memory_role: options.memoryRole
287
322
  });
288
323
  if (options.toon === true) {
289
324
  return searchResponseToToon(compactSearchResponse(body));
@@ -299,6 +334,50 @@ program
299
334
  const memoryCommand = program
300
335
  .command('memory')
301
336
  .description('Memory-specific commands');
337
+ memoryCommand
338
+ .command('groom')
339
+ .description('Preview or archive stale session-context memories for the authenticated client')
340
+ .option('--dry-run', 'preview without mutating')
341
+ .option('--older-than <duration>', 'only include memories older than this (e.g. 15m, 2h, 7d)', '7d')
342
+ .option('--limit <limit>', 'maximum candidates', '50')
343
+ .option('--topic <topic>', 'filter by topic')
344
+ .option('--session-id <sessionId>', 'filter by session id')
345
+ .option('--tag <tag>', 'filter by tag (repeatable)', (value, previous = []) => {
346
+ previous.push(value);
347
+ return previous;
348
+ }, [])
349
+ .option('--yes', 'confirm archive mutation')
350
+ .action(async (options, command) => {
351
+ await runWithClient(command, async (client, json) => {
352
+ const limit = Number.parseInt(options.limit, 10);
353
+ if (!Number.isInteger(limit) || limit <= 0) {
354
+ throw new AppError(ErrorCode.VALIDATION, '--limit must be a positive integer');
355
+ }
356
+ if (options.dryRun !== true && options.yes !== true) {
357
+ throw new AppError(ErrorCode.VALIDATION, '--yes is required outside dry-run');
358
+ }
359
+ const body = await client.groomSessionContext({
360
+ dry_run: options.dryRun === true,
361
+ confirmed: options.dryRun === true ? undefined : true,
362
+ older_than_ms: parseDurationMs(options.olderThan),
363
+ limit,
364
+ topic: options.topic,
365
+ session_id: options.sessionId,
366
+ tags: options.tag
367
+ });
368
+ if (json) {
369
+ return body;
370
+ }
371
+ if (body.dryRun) {
372
+ return formatGroomCandidates(body.eligible ?? []);
373
+ }
374
+ return [
375
+ `Archived ${body.archived} session-context memories`,
376
+ ` promoted: ${body.promoted}`,
377
+ ` skipped: ${body.skipped}`
378
+ ];
379
+ });
380
+ });
302
381
  memoryCommand
303
382
  .command('session-context')
304
383
  .alias('session')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ivotoby/postgram-cli",
3
- "version": "1.30.0",
3
+ "version": "1.30.1",
4
4
  "description": "Postgram CLI — store, search, and manage entities from the command line",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,6 +33,7 @@
33
33
  },
34
34
  "scripts": {
35
35
  "build": "tsc -p tsconfig.build.json",
36
+ "typecheck": "tsc -p tsconfig.build.json --noEmit",
36
37
  "prepublishOnly": "npm run build",
37
38
  "test": "vitest run",
38
39
  "test:watch": "vitest",