agent-remnote 1.2.0 → 1.3.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/main.js CHANGED
@@ -75207,16 +75207,16 @@ function writeSuccess(params3) {
75207
75207
  if (config3.quiet)
75208
75208
  return;
75209
75209
  if (config3.format === "ids") {
75210
- if (!params3.ids || params3.ids.length === 0) {
75210
+ if (!params3.ids) {
75211
75211
  return yield* fail8(new CliError({
75212
75212
  code: "INVALID_ARGS",
75213
75213
  message: "This command does not support --ids output",
75214
75214
  exitCode: 2
75215
75215
  }));
75216
75216
  }
75217
- yield* out.stdout(`${params3.ids.join(`
75217
+ yield* out.stdout(params3.ids.length > 0 ? `${params3.ids.join(`
75218
75218
  `)}
75219
- `);
75219
+ ` : "");
75220
75220
  return;
75221
75221
  }
75222
75222
  if (params3.data && typeof params3.data === "object") {
@@ -75987,6 +75987,90 @@ var migration6 = {
75987
75987
  }
75988
75988
  };
75989
75989
 
75990
+ // src/internal/store/migrations/0008-add-automation-skeleton.ts
75991
+ var TASK_DEFS_TABLE_SQL = `CREATE TABLE IF NOT EXISTS task_defs (
75992
+ task_id TEXT PRIMARY KEY,
75993
+ task_kind TEXT NOT NULL,
75994
+ config_json TEXT NOT NULL DEFAULT '{}',
75995
+ created_at INTEGER NOT NULL,
75996
+ updated_at INTEGER NOT NULL
75997
+ );`;
75998
+ var TRIGGER_RULES_TABLE_SQL = `CREATE TABLE IF NOT EXISTS trigger_rules (
75999
+ trigger_id TEXT PRIMARY KEY,
76000
+ trigger_kind TEXT NOT NULL,
76001
+ task_id TEXT NOT NULL,
76002
+ enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
76003
+ match_json TEXT NOT NULL DEFAULT '{}',
76004
+ created_at INTEGER NOT NULL,
76005
+ updated_at INTEGER NOT NULL,
76006
+ FOREIGN KEY (task_id) REFERENCES task_defs(task_id) ON DELETE RESTRICT
76007
+ );`;
76008
+ var EVENT_EVENTS_TABLE_SQL = `CREATE TABLE IF NOT EXISTS event_events (
76009
+ event_id TEXT PRIMARY KEY,
76010
+ event_kind TEXT NOT NULL,
76011
+ source_rem_id TEXT,
76012
+ source_tag_id TEXT,
76013
+ dedupe_key TEXT NOT NULL UNIQUE,
76014
+ payload_json TEXT NOT NULL DEFAULT '{}',
76015
+ created_at INTEGER NOT NULL
76016
+ );`;
76017
+ var TASK_RUNS_TABLE_SQL = `CREATE TABLE IF NOT EXISTS task_runs (
76018
+ run_id TEXT PRIMARY KEY,
76019
+ task_id TEXT NOT NULL,
76020
+ trigger_id TEXT,
76021
+ event_id TEXT,
76022
+ target_rem_id TEXT NOT NULL,
76023
+ result_rem_id TEXT,
76024
+ queue_txn_id TEXT,
76025
+ status TEXT NOT NULL CHECK (status IN ('pending','running','succeeded','failed','aborted')),
76026
+ detail_json TEXT NOT NULL DEFAULT '{}',
76027
+ created_at INTEGER NOT NULL,
76028
+ updated_at INTEGER NOT NULL,
76029
+ finished_at INTEGER,
76030
+ FOREIGN KEY (task_id) REFERENCES task_defs(task_id) ON DELETE RESTRICT,
76031
+ FOREIGN KEY (trigger_id) REFERENCES trigger_rules(trigger_id) ON DELETE SET NULL,
76032
+ FOREIGN KEY (event_id) REFERENCES event_events(event_id) ON DELETE SET NULL,
76033
+ FOREIGN KEY (queue_txn_id) REFERENCES queue_txns(txn_id) ON DELETE SET NULL
76034
+ );`;
76035
+ var IDX_TRIGGER_RULES_TASK_ID_SQL = `CREATE INDEX IF NOT EXISTS idx_trigger_rules_task_id ON trigger_rules(task_id);`;
76036
+ var IDX_TASK_RUNS_TASK_ID_SQL = `CREATE INDEX IF NOT EXISTS idx_task_runs_task_id ON task_runs(task_id, updated_at DESC);`;
76037
+ var IDX_TASK_RUNS_QUEUE_TXN_ID_SQL = `CREATE INDEX IF NOT EXISTS idx_task_runs_queue_txn_id ON task_runs(queue_txn_id);`;
76038
+ var IDX_EVENT_EVENTS_SOURCE_REM_SQL = `CREATE INDEX IF NOT EXISTS idx_event_events_source_rem_id ON event_events(source_rem_id, created_at DESC);`;
76039
+ var AUTOMATION_SKELETON_SQL = [
76040
+ TASK_DEFS_TABLE_SQL,
76041
+ TRIGGER_RULES_TABLE_SQL,
76042
+ EVENT_EVENTS_TABLE_SQL,
76043
+ TASK_RUNS_TABLE_SQL,
76044
+ IDX_TRIGGER_RULES_TASK_ID_SQL,
76045
+ IDX_TASK_RUNS_TASK_ID_SQL,
76046
+ IDX_TASK_RUNS_QUEUE_TXN_ID_SQL,
76047
+ IDX_EVENT_EVENTS_SOURCE_REM_SQL
76048
+ ];
76049
+ var migration7 = {
76050
+ version: 8,
76051
+ name: "add_automation_skeleton",
76052
+ checksumInput: AUTOMATION_SKELETON_SQL.join(`
76053
+ `),
76054
+ apply: (db) => {
76055
+ db.exec(AUTOMATION_SKELETON_SQL.join(`
76056
+ `));
76057
+ }
76058
+ };
76059
+
76060
+ // src/internal/store/migrations/0009-add-task-run-fk-indexes.ts
76061
+ var IDX_TASK_RUNS_TRIGGER_ID_SQL = `CREATE INDEX IF NOT EXISTS idx_task_runs_trigger_id ON task_runs(trigger_id);`;
76062
+ var IDX_TASK_RUNS_EVENT_ID_SQL = `CREATE INDEX IF NOT EXISTS idx_task_runs_event_id ON task_runs(event_id);`;
76063
+ var migration8 = {
76064
+ version: 9,
76065
+ name: "add_task_run_fk_indexes",
76066
+ checksumInput: [IDX_TASK_RUNS_TRIGGER_ID_SQL, IDX_TASK_RUNS_EVENT_ID_SQL].join(`
76067
+ `),
76068
+ apply: (db) => {
76069
+ db.exec(IDX_TASK_RUNS_TRIGGER_ID_SQL);
76070
+ db.exec(IDX_TASK_RUNS_EVENT_ID_SQL);
76071
+ }
76072
+ };
76073
+
75990
76074
  // src/internal/store/migrations/index.ts
75991
76075
  var BACKUP_ARTIFACTS_TABLE_SQL = `CREATE TABLE IF NOT EXISTS backup_artifacts (
75992
76076
  source_op_id TEXT PRIMARY KEY,
@@ -76018,7 +76102,7 @@ var m0007 = {
76018
76102
  db.exec(UQ_BACKUP_ARTIFACTS_REM_SQL);
76019
76103
  }
76020
76104
  };
76021
- var migrationSpecs = [migration, migration2, migration3, migration4, migration5, migration6, m0007];
76105
+ var migrationSpecs = [migration, migration2, migration3, migration4, migration5, migration6, m0007, migration7, migration8];
76022
76106
 
76023
76107
  // src/internal/store/db.ts
76024
76108
  class StoreSchemaError extends Error {
@@ -76184,6 +76268,72 @@ CREATE INDEX IF NOT EXISTS idx_backup_artifacts_kind
76184
76268
  CREATE UNIQUE INDEX IF NOT EXISTS uq_backup_artifacts_backup_rem_id
76185
76269
  ON backup_artifacts(backup_rem_id)
76186
76270
  WHERE backup_rem_id IS NOT NULL;
76271
+
76272
+ CREATE TABLE IF NOT EXISTS task_defs (
76273
+ task_id TEXT PRIMARY KEY,
76274
+ task_kind TEXT NOT NULL,
76275
+ config_json TEXT NOT NULL DEFAULT '{}',
76276
+ created_at INTEGER NOT NULL,
76277
+ updated_at INTEGER NOT NULL
76278
+ );
76279
+
76280
+ CREATE TABLE IF NOT EXISTS trigger_rules (
76281
+ trigger_id TEXT PRIMARY KEY,
76282
+ trigger_kind TEXT NOT NULL,
76283
+ task_id TEXT NOT NULL,
76284
+ enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
76285
+ match_json TEXT NOT NULL DEFAULT '{}',
76286
+ created_at INTEGER NOT NULL,
76287
+ updated_at INTEGER NOT NULL,
76288
+ FOREIGN KEY (task_id) REFERENCES task_defs(task_id) ON DELETE RESTRICT
76289
+ );
76290
+
76291
+ CREATE INDEX IF NOT EXISTS idx_trigger_rules_task_id
76292
+ ON trigger_rules(task_id);
76293
+
76294
+ CREATE TABLE IF NOT EXISTS event_events (
76295
+ event_id TEXT PRIMARY KEY,
76296
+ event_kind TEXT NOT NULL,
76297
+ source_rem_id TEXT,
76298
+ source_tag_id TEXT,
76299
+ dedupe_key TEXT NOT NULL UNIQUE,
76300
+ payload_json TEXT NOT NULL DEFAULT '{}',
76301
+ created_at INTEGER NOT NULL
76302
+ );
76303
+
76304
+ CREATE INDEX IF NOT EXISTS idx_event_events_source_rem_id
76305
+ ON event_events(source_rem_id, created_at DESC);
76306
+
76307
+ CREATE TABLE IF NOT EXISTS task_runs (
76308
+ run_id TEXT PRIMARY KEY,
76309
+ task_id TEXT NOT NULL,
76310
+ trigger_id TEXT,
76311
+ event_id TEXT,
76312
+ target_rem_id TEXT NOT NULL,
76313
+ result_rem_id TEXT,
76314
+ queue_txn_id TEXT,
76315
+ status TEXT NOT NULL CHECK (status IN ('pending','running','succeeded','failed','aborted')),
76316
+ detail_json TEXT NOT NULL DEFAULT '{}',
76317
+ created_at INTEGER NOT NULL,
76318
+ updated_at INTEGER NOT NULL,
76319
+ finished_at INTEGER,
76320
+ FOREIGN KEY (task_id) REFERENCES task_defs(task_id) ON DELETE RESTRICT,
76321
+ FOREIGN KEY (trigger_id) REFERENCES trigger_rules(trigger_id) ON DELETE SET NULL,
76322
+ FOREIGN KEY (event_id) REFERENCES event_events(event_id) ON DELETE SET NULL,
76323
+ FOREIGN KEY (queue_txn_id) REFERENCES queue_txns(txn_id) ON DELETE SET NULL
76324
+ );
76325
+
76326
+ CREATE INDEX IF NOT EXISTS idx_task_runs_task_id
76327
+ ON task_runs(task_id, updated_at DESC);
76328
+
76329
+ CREATE INDEX IF NOT EXISTS idx_task_runs_trigger_id
76330
+ ON task_runs(trigger_id);
76331
+
76332
+ CREATE INDEX IF NOT EXISTS idx_task_runs_event_id
76333
+ ON task_runs(event_id);
76334
+
76335
+ CREATE INDEX IF NOT EXISTS idx_task_runs_queue_txn_id
76336
+ ON task_runs(queue_txn_id);
76187
76337
  `;
76188
76338
  function absoluteDefaultStorePath() {
76189
76339
  return path12.join(homeDir(), ".agent-remnote", "store.sqlite");
@@ -76380,17 +76530,17 @@ function readAppliedMigrations(db) {
76380
76530
  return new Map;
76381
76531
  }
76382
76532
  }
76383
- function ensureMigrationRecorded(db, migration7, appliedAt, appVersion) {
76384
- const existing = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration7.version);
76533
+ function ensureMigrationRecorded(db, migration9, appliedAt, appVersion) {
76534
+ const existing = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration9.version);
76385
76535
  if (existing) {
76386
76536
  const actualChecksum = typeof existing?.checksum === "string" ? existing.checksum : String(existing?.checksum ?? "");
76387
- if (actualChecksum !== migration7.checksum) {
76537
+ if (actualChecksum !== migration9.checksum) {
76388
76538
  throw new StoreSchemaError({
76389
76539
  code: "STORE_SCHEMA_INVALID",
76390
76540
  message: "Store database migration drift detected",
76391
76541
  details: {
76392
- version: migration7.version,
76393
- expected: { name: migration7.name, checksum: migration7.checksum },
76542
+ version: migration9.version,
76543
+ expected: { name: migration9.name, checksum: migration9.checksum },
76394
76544
  actual: { name: String(existing?.name ?? ""), checksum: actualChecksum }
76395
76545
  },
76396
76546
  nextActions: ["Upgrade `agent-remnote` to a newer version", "agent-remnote doctor"]
@@ -76401,9 +76551,9 @@ function ensureMigrationRecorded(db, migration7, appliedAt, appVersion) {
76401
76551
  try {
76402
76552
  db.prepare(`INSERT INTO store_migrations(version, name, checksum, applied_at, app_version)
76403
76553
  VALUES(@version, @name, @checksum, @applied_at, @app_version)`).run({
76404
- version: migration7.version,
76405
- name: migration7.name,
76406
- checksum: migration7.checksum,
76554
+ version: migration9.version,
76555
+ name: migration9.name,
76556
+ checksum: migration9.checksum,
76407
76557
  applied_at: appliedAt,
76408
76558
  app_version: appVersion
76409
76559
  });
@@ -76411,15 +76561,15 @@ function ensureMigrationRecorded(db, migration7, appliedAt, appVersion) {
76411
76561
  const code2 = String(e?.code || "");
76412
76562
  if (code2 !== "SQLITE_CONSTRAINT" && code2 !== "SQLITE_CONSTRAINT_PRIMARYKEY" && code2 !== "SQLITE_CONSTRAINT_UNIQUE")
76413
76563
  throw e;
76414
- const again = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration7.version);
76564
+ const again = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration9.version);
76415
76565
  const actualChecksum = typeof again?.checksum === "string" ? again.checksum : String(again?.checksum ?? "");
76416
- if (actualChecksum !== migration7.checksum) {
76566
+ if (actualChecksum !== migration9.checksum) {
76417
76567
  throw new StoreSchemaError({
76418
76568
  code: "STORE_SCHEMA_INVALID",
76419
76569
  message: "Store database migration drift detected",
76420
76570
  details: {
76421
- version: migration7.version,
76422
- expected: { name: migration7.name, checksum: migration7.checksum },
76571
+ version: migration9.version,
76572
+ expected: { name: migration9.name, checksum: migration9.checksum },
76423
76573
  actual: { name: String(again?.name ?? ""), checksum: actualChecksum }
76424
76574
  },
76425
76575
  nextActions: ["Upgrade `agent-remnote` to a newer version", "agent-remnote doctor"]
@@ -76454,8 +76604,8 @@ function ensureAuditUpTo(db, version) {
76454
76604
  }
76455
76605
  }
76456
76606
  function applyMigration(db, targetVersion) {
76457
- const migration7 = MIGRATION_PLAN.migrations[targetVersion - 1];
76458
- if (!migration7 || migration7.version !== targetVersion) {
76607
+ const migration9 = MIGRATION_PLAN.migrations[targetVersion - 1];
76608
+ if (!migration9 || migration9.version !== targetVersion) {
76459
76609
  throw new StoreSchemaError({
76460
76610
  code: "STORE_SCHEMA_UNKNOWN",
76461
76611
  message: `Unknown store migration target: ${targetVersion}`,
@@ -76463,8 +76613,8 @@ function applyMigration(db, targetVersion) {
76463
76613
  nextActions: ["Report this as a bug"]
76464
76614
  });
76465
76615
  }
76466
- migration7.apply(db);
76467
- ensureMigrationRecorded(db, migration7, Date.now(), appVersionForAudit());
76616
+ migration9.apply(db);
76617
+ ensureMigrationRecorded(db, migration9, Date.now(), appVersionForAudit());
76468
76618
  }
76469
76619
  function migrate(db, resolvedPath) {
76470
76620
  const initial = readUserVersion(db);
@@ -76793,7 +76943,7 @@ var OP_CATALOG = {
76793
76943
  op_type: "replace_selection_with_markdown",
76794
76944
  payload: {
76795
76945
  required: ["markdown"],
76796
- optional: ["target", "require_same_parent", "require_contiguous", "portal_id"]
76946
+ optional: ["target", "require_same_parent", "require_contiguous", "portal_id", "assertions"]
76797
76947
  },
76798
76948
  description: "Replace a selection of Rems with Markdown.",
76799
76949
  id_fields: ["target.rem_ids[]", "portal_id"]
@@ -77183,10 +77333,13 @@ function commitTxn(db, txn_id) {
77183
77333
  upd.run({ t, txn_id });
77184
77334
  }
77185
77335
  function enqueueTxn(db, ops, options6) {
77186
- const txn_id = createTxn(db, options6);
77187
- addOps(db, txn_id, ops);
77188
- commitTxn(db, txn_id);
77189
- return txn_id;
77336
+ const trx = db.transaction(() => {
77337
+ const txn_id = createTxn(db, options6);
77338
+ addOps(db, txn_id, ops);
77339
+ commitTxn(db, txn_id);
77340
+ return txn_id;
77341
+ });
77342
+ return trx();
77190
77343
  }
77191
77344
 
77192
77345
  class IdMapConflictError extends Error {
@@ -85895,14 +86048,24 @@ function fetchOutlineNodes(db, options6) {
85895
86048
  })();
85896
86049
  const rows = stmt.all({ rootId: options6.rootId, maxDepth: options6.maxDepth });
85897
86050
  const nodes = [];
86051
+ const targetStmt = db.prepare("SELECT doc FROM quanta WHERE _id = ?");
86052
+ const targetCache = new Map;
85898
86053
  for (const row of rows) {
85899
86054
  const doc = safeJsonParse(row.doc);
85900
86055
  const rawKey = doc?.key;
86056
+ const targetDescriptor = findOutlineTargetDescriptor(doc, rawKey);
86057
+ const target2 = targetDescriptor ? targetCache.get(targetDescriptor.id) ?? (() => {
86058
+ const resolved = resolveOutlineTarget(targetStmt, db, targetDescriptor.id);
86059
+ targetCache.set(targetDescriptor.id, resolved);
86060
+ return resolved;
86061
+ })() : null;
86062
+ const kind = targetDescriptor?.kind === "portal" ? "portal" : "rem";
85901
86063
  const keySummary = summarizeKey(rawKey, db, {
85902
86064
  expand: options6.expandReferences,
85903
86065
  maxDepth: options6.maxReferenceDepth
85904
86066
  });
85905
- if (!options6.includeEmpty && !keySummary.text && row.depth !== 0) {
86067
+ const text14 = renderOutlineNodeText(kind, keySummary.text, target2);
86068
+ if (!options6.includeEmpty && !text14 && row.depth !== 0) {
85906
86069
  continue;
85907
86070
  }
85908
86071
  const isSystemRem = SYSTEM_REM_IDS.has(row.id) || SYSTEM_REM_KEYS.has(keySummary.text);
@@ -85919,9 +86082,11 @@ function fetchOutlineNodes(db, options6) {
85919
86082
  nodes.push({
85920
86083
  id: row.id,
85921
86084
  depth: row.depth,
86085
+ kind,
85922
86086
  sortKey: typeof doc?.f === "string" ? doc.f : null,
85923
86087
  parentId: typeof doc?.parent === "string" ? doc.parent : null,
85924
- text: keySummary.text,
86088
+ text: text14,
86089
+ target: target2,
85925
86090
  references: keySummary.references,
85926
86091
  rawKey
85927
86092
  });
@@ -85932,7 +86097,9 @@ function simplifyOutlineNodes(nodes) {
85932
86097
  return nodes.map((node) => ({
85933
86098
  id: node.id,
85934
86099
  depth: node.depth,
86100
+ kind: node.kind,
85935
86101
  text: node.text,
86102
+ target: node.target,
85936
86103
  references: node.references
85937
86104
  }));
85938
86105
  }
@@ -85944,6 +86111,72 @@ function toMarkdown(nodes) {
85944
86111
  }).join(`
85945
86112
  `);
85946
86113
  }
86114
+ function findOutlineTargetDescriptor(doc, value8) {
86115
+ const docType = typeof doc?.type === "number" ? doc.type : typeof doc?.type === "string" ? Number(doc.type) : NaN;
86116
+ const pdRaw = doc?.pd;
86117
+ if (docType === 6 && pdRaw && typeof pdRaw === "object" && !Array.isArray(pdRaw)) {
86118
+ const pdKeys = Object.keys(pdRaw).map((key) => key.trim()).filter(Boolean);
86119
+ if (pdKeys.length > 0) {
86120
+ return { kind: "portal", id: pdKeys[0] };
86121
+ }
86122
+ }
86123
+ const referenceFallback = (() => {
86124
+ if (!Array.isArray(value8) || value8.length !== 1)
86125
+ return null;
86126
+ const only = value8[0];
86127
+ if (!only || typeof only !== "object")
86128
+ return null;
86129
+ const obj = only;
86130
+ const tokenType = typeof obj.i === "string" ? obj.i : "";
86131
+ const targetId = typeof obj._id === "string" ? obj._id.trim() : "";
86132
+ return tokenType === "q" && targetId ? { kind: "reference", id: targetId } : null;
86133
+ })();
86134
+ const visit2 = (input) => {
86135
+ if (Array.isArray(input)) {
86136
+ for (const item of input) {
86137
+ const found = visit2(item);
86138
+ if (found?.kind === "portal")
86139
+ return found;
86140
+ }
86141
+ return null;
86142
+ }
86143
+ if (input && typeof input === "object") {
86144
+ const obj = input;
86145
+ const tokenType = typeof obj.i === "string" ? obj.i : "";
86146
+ const targetId = typeof obj._id === "string" ? obj._id.trim() : "";
86147
+ if (tokenType === "p" && targetId) {
86148
+ return { kind: "portal", id: targetId };
86149
+ }
86150
+ for (const child of Object.values(obj)) {
86151
+ const found = visit2(child);
86152
+ if (found?.kind === "portal")
86153
+ return found;
86154
+ }
86155
+ }
86156
+ return null;
86157
+ };
86158
+ const portal = visit2(value8);
86159
+ return portal ?? referenceFallback;
86160
+ }
86161
+ function resolveOutlineTarget(stmt, db, targetId) {
86162
+ const row = stmt.get(targetId);
86163
+ if (!row?.doc) {
86164
+ return { id: targetId, text: null, resolved: false };
86165
+ }
86166
+ const parsed = safeJsonParse(row.doc);
86167
+ const summary6 = summarizeKey(parsed?.key, db, { expand: false, maxDepth: 0 });
86168
+ const text14 = typeof summary6.text === "string" ? summary6.text : null;
86169
+ return { id: targetId, text: text14, resolved: true };
86170
+ }
86171
+ function renderOutlineNodeText(kind, fallbackText, target2) {
86172
+ if (kind === "portal" && target2) {
86173
+ if (!target2.resolved) {
86174
+ return `Portal -> {ref:${target2.id}}`;
86175
+ }
86176
+ return `Portal -> ${target2.text && target2.text.length > 0 ? target2.text : "(empty)"}`;
86177
+ }
86178
+ return fallbackText;
86179
+ }
85947
86180
 
85948
86181
  // src/internal/remdb-tools/findRemsByReference.ts
85949
86182
  var inputShape4 = {
@@ -90168,6 +90401,174 @@ async function executeResolveRemPage(params3) {
90168
90401
  markdown
90169
90402
  };
90170
90403
  }
90404
+ // src/internal/remdb-tools/summarizeRecentActivity.ts
90405
+ function asInt(value8) {
90406
+ if (typeof value8 === "number" && Number.isFinite(value8))
90407
+ return Math.trunc(value8);
90408
+ if (typeof value8 === "string") {
90409
+ const n = Number(value8);
90410
+ if (Number.isFinite(n))
90411
+ return Math.trunc(n);
90412
+ }
90413
+ return null;
90414
+ }
90415
+ function cleanTitle(input) {
90416
+ if (typeof input !== "string")
90417
+ return "";
90418
+ return input.split(/\s+/).join(" ").trim();
90419
+ }
90420
+ function assertTimezone(timezone) {
90421
+ const effective = timezone.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
90422
+ new Intl.DateTimeFormat("en-CA", { timeZone: effective, year: "numeric", month: "2-digit", day: "2-digit" });
90423
+ return effective;
90424
+ }
90425
+ function dayKeyFromMs(ms, timezone) {
90426
+ const parts2 = new Intl.DateTimeFormat("en-CA", {
90427
+ timeZone: timezone,
90428
+ year: "numeric",
90429
+ month: "2-digit",
90430
+ day: "2-digit"
90431
+ }).formatToParts(new Date(ms));
90432
+ const year = parts2.find((part) => part.type === "year")?.value ?? "0000";
90433
+ const month = parts2.find((part) => part.type === "month")?.value ?? "00";
90434
+ const day = parts2.find((part) => part.type === "day")?.value ?? "00";
90435
+ return `${year}-${month}-${day}`;
90436
+ }
90437
+ function buildCounts(items) {
90438
+ const created = items.filter((item) => item.activity_kind === "created").length;
90439
+ const modifiedExisting = items.filter((item) => item.activity_kind === "modified_existing").length;
90440
+ return {
90441
+ total: items.length,
90442
+ created,
90443
+ modified_existing: modifiedExisting
90444
+ };
90445
+ }
90446
+ function toAggregateItems(items) {
90447
+ return items.map(({ activity_at: _activityAt, ...item }) => item);
90448
+ }
90449
+ function summarizeByDay(items, timezone, limit) {
90450
+ const groups = new Map;
90451
+ for (const item of items) {
90452
+ const key = dayKeyFromMs(item.activity_at, timezone);
90453
+ const existing = groups.get(key);
90454
+ if (existing)
90455
+ existing.push(item);
90456
+ else
90457
+ groups.set(key, [item]);
90458
+ }
90459
+ return Array.from(groups.entries()).sort((a, b) => b[0].localeCompare(a[0])).slice(0, limit).map(([key, group4]) => ({
90460
+ dimension: "day",
90461
+ key,
90462
+ counts: buildCounts(group4),
90463
+ samples: toAggregateItems(group4.slice(0, 3)),
90464
+ timezone
90465
+ }));
90466
+ }
90467
+ function summarizeByParent(items, limit) {
90468
+ const groups = new Map;
90469
+ for (const item of items) {
90470
+ const key = item.parent_id ?? "(root)";
90471
+ const existing = groups.get(key);
90472
+ if (existing)
90473
+ existing.push(item);
90474
+ else
90475
+ groups.set(key, [item]);
90476
+ }
90477
+ return Array.from(groups.entries()).sort((a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0])).slice(0, limit).map(([key, group4]) => ({
90478
+ dimension: "parent",
90479
+ key,
90480
+ counts: buildCounts(group4),
90481
+ samples: toAggregateItems(group4.slice(0, 3)),
90482
+ parent_id: group4[0]?.parent_id ?? null,
90483
+ parent_preview: group4[0]?.parent_preview ?? null
90484
+ }));
90485
+ }
90486
+ function executeSummarizeRecentActivity(db, params3) {
90487
+ if (!Number.isInteger(params3.days) || params3.days < 1) {
90488
+ throw new RangeError("days must be an integer >= 1");
90489
+ }
90490
+ if (!Number.isInteger(params3.itemLimit) || params3.itemLimit < 1) {
90491
+ throw new RangeError("itemLimit must be an integer >= 1");
90492
+ }
90493
+ if (!Number.isInteger(params3.aggregateLimit) || params3.aggregateLimit < 1) {
90494
+ throw new RangeError("aggregateLimit must be an integer >= 1");
90495
+ }
90496
+ if (params3.kind !== "all" && params3.kind !== "created" && params3.kind !== "modified_existing") {
90497
+ throw new RangeError("kind must be 'all', 'created', or 'modified_existing'");
90498
+ }
90499
+ const aggregateDimensions = Array.from(new Set(params3.aggregates));
90500
+ if (aggregateDimensions.some((dimension) => dimension !== "day" && dimension !== "parent")) {
90501
+ throw new RangeError("aggregates must contain only 'day' or 'parent'");
90502
+ }
90503
+ const now2 = params3.now ?? Date.now();
90504
+ const cutoffMs = now2 - params3.days * 24 * 60 * 60 * 1000;
90505
+ const timezone = assertTimezone(params3.timezone);
90506
+ const rows = db.prepare(`
90507
+ select
90508
+ q._id as id,
90509
+ cast(json_extract(q.doc, '$.createdAt') as integer) as createdAt,
90510
+ cast(coalesce(json_extract(q.doc, '$.m'), json_extract(q.doc, '$.createdAt')) as integer) as updatedAt,
90511
+ json_extract(r.doc, '$.r') as preview,
90512
+ json_extract(q.doc, '$.parent') as parentId,
90513
+ json_extract(pr.doc, '$.r') as parentPreview
90514
+ from quanta q
90515
+ join remsSearchInfos r on r.id = q._id
90516
+ left join remsSearchInfos pr on pr.id = json_extract(q.doc, '$.parent')
90517
+ where
90518
+ cast(json_extract(q.doc, '$.createdAt') as integer) >= @cutoffMs
90519
+ or cast(coalesce(json_extract(q.doc, '$.m'), json_extract(q.doc, '$.createdAt')) as integer) >= @cutoffMs
90520
+ `).all({ cutoffMs });
90521
+ const candidates = rows.map((row) => {
90522
+ const createdAt = asInt(row.createdAt);
90523
+ const updatedAt = asInt(row.updatedAt);
90524
+ if (createdAt === null || updatedAt === null) {
90525
+ return null;
90526
+ }
90527
+ const base = {
90528
+ id: String(row.id),
90529
+ created_at: createdAt,
90530
+ updated_at: updatedAt,
90531
+ preview: cleanTitle(row.preview) || "<no preview>",
90532
+ parent_id: typeof row.parentId === "string" && row.parentId.trim() ? row.parentId.trim() : null,
90533
+ parent_preview: cleanTitle(row.parentPreview) || null
90534
+ };
90535
+ if (base.created_at >= cutoffMs) {
90536
+ return {
90537
+ ...base,
90538
+ activity_kind: "created",
90539
+ activity_at: base.created_at
90540
+ };
90541
+ }
90542
+ if (base.updated_at >= cutoffMs && base.created_at < cutoffMs) {
90543
+ return {
90544
+ ...base,
90545
+ activity_kind: "modified_existing",
90546
+ activity_at: base.updated_at
90547
+ };
90548
+ }
90549
+ return null;
90550
+ }).filter((value8) => value8 !== null).filter((item) => params3.kind === "all" || item.activity_kind === params3.kind).sort((a, b) => b.activity_at - a.activity_at || a.id.localeCompare(b.id));
90551
+ const counts = buildCounts(candidates);
90552
+ const items = toAggregateItems(candidates.slice(0, params3.itemLimit));
90553
+ const aggregates = [];
90554
+ for (const dimension of aggregateDimensions) {
90555
+ if (dimension === "day") {
90556
+ aggregates.push(...summarizeByDay(candidates, timezone, params3.aggregateLimit));
90557
+ continue;
90558
+ }
90559
+ if (dimension === "parent") {
90560
+ aggregates.push(...summarizeByParent(candidates, params3.aggregateLimit));
90561
+ }
90562
+ }
90563
+ return {
90564
+ days: params3.days,
90565
+ timezone,
90566
+ cutoff_ms: cutoffMs,
90567
+ counts,
90568
+ items,
90569
+ aggregates
90570
+ };
90571
+ }
90171
90572
  // src/services/Queue.ts
90172
90573
  class Queue extends Tag2("Queue")() {
90173
90574
  }
@@ -93919,6 +94320,77 @@ var ACTIONS = {
93919
94320
  return { ops: [{ type: "replace_children_with_markdown", payload: { parent_id: rem_id, markdown: "" } }] };
93920
94321
  }
93921
94322
  },
94323
+ "portal.create": {
94324
+ opType: "create_portal",
94325
+ supportsAs: false,
94326
+ aliasRefAllowlist: ["parent_id", "target_rem_id"],
94327
+ compile: ({ input }) => {
94328
+ const parent_id = input.parent_id;
94329
+ const target_rem_id = input.target_rem_id;
94330
+ if (typeof parent_id !== "string" || !parent_id.trim()) {
94331
+ throw new Error("portal.create requires input.parent_id");
94332
+ }
94333
+ if (typeof target_rem_id !== "string" || !target_rem_id.trim()) {
94334
+ throw new Error("portal.create requires input.target_rem_id");
94335
+ }
94336
+ const payload = { parent_id, target_rem_id };
94337
+ if (typeof input.position === "number")
94338
+ payload.position = input.position;
94339
+ return { ops: [{ type: "create_portal", payload }] };
94340
+ }
94341
+ },
94342
+ "rem.replace": {
94343
+ opType: "replace_children_with_markdown",
94344
+ supportsAs: false,
94345
+ aliasRefAllowlist: ["rem_ids[]"],
94346
+ compile: ({ input }) => {
94347
+ const surface = input.surface;
94348
+ const markdown = input.markdown;
94349
+ const rem_ids = Array.isArray(input.rem_ids) ? input.rem_ids.filter((value8) => typeof value8 === "string" && value8.trim().length > 0) : [];
94350
+ if (surface !== "children" && surface !== "self") {
94351
+ throw new Error("rem.replace requires input.surface to be 'children' or 'self'");
94352
+ }
94353
+ if (typeof markdown !== "string") {
94354
+ throw new Error("rem.replace requires input.markdown");
94355
+ }
94356
+ if (rem_ids.length === 0) {
94357
+ throw new Error("rem.replace requires input.rem_ids[]");
94358
+ }
94359
+ const assertions = Array.isArray(input.assertions) ? input.assertions : [];
94360
+ const validAssertions = assertions.every((value8) => typeof value8 === "string" && WRITE_STRUCTURE_ASSERTIONS.has(value8));
94361
+ if (!validAssertions) {
94362
+ throw new Error("rem.replace input.assertions must only include: single-root, preserve-anchor, no-literal-bullet");
94363
+ }
94364
+ if (surface === "self" && assertions.includes("preserve-anchor")) {
94365
+ throw new Error("rem.replace input.surface=self does not support input.assertions preserve-anchor");
94366
+ }
94367
+ if (surface === "children") {
94368
+ if (rem_ids.length !== 1) {
94369
+ throw new Error("rem.replace input.surface=children requires exactly one target");
94370
+ }
94371
+ const payload = { parent_id: rem_ids[0], markdown };
94372
+ if (assertions.length > 0)
94373
+ payload.assertions = assertions;
94374
+ return {
94375
+ ops: [{ type: "replace_children_with_markdown", payload }]
94376
+ };
94377
+ }
94378
+ return {
94379
+ ops: [
94380
+ {
94381
+ type: "replace_selection_with_markdown",
94382
+ payload: {
94383
+ markdown,
94384
+ target: { mode: "explicit", rem_ids },
94385
+ require_same_parent: true,
94386
+ require_contiguous: true,
94387
+ ...assertions.length > 0 ? { assertions } : {}
94388
+ }
94389
+ }
94390
+ ]
94391
+ };
94392
+ }
94393
+ },
93922
94394
  rem: {
93923
94395
  opType: "",
93924
94396
  supportsAs: false,
@@ -94126,6 +94598,81 @@ function decideOutlineWriteShape(params3) {
94126
94598
  top_level_roots: topLevelRoots
94127
94599
  };
94128
94600
  }
94601
+ // src/lib/text.ts
94602
+ function trimBoundaryBlankLines(input) {
94603
+ const normalized = input.replace(/\r\n?/g, `
94604
+ `);
94605
+ const lines3 = normalized.split(`
94606
+ `);
94607
+ let start4 = 0;
94608
+ while (start4 < lines3.length && lines3[start4]?.trim().length === 0)
94609
+ start4 += 1;
94610
+ if (start4 >= lines3.length)
94611
+ return "";
94612
+ let end6 = lines3.length - 1;
94613
+ while (end6 >= start4 && lines3[end6]?.trim().length === 0)
94614
+ end6 -= 1;
94615
+ if (end6 < start4)
94616
+ return "";
94617
+ let inFence = false;
94618
+ for (let i = start4;i <= end6; i += 1) {
94619
+ if (/^\s*```/.test(lines3[i] ?? ""))
94620
+ inFence = !inFence;
94621
+ }
94622
+ const keptEnd = inFence ? lines3.length - 1 : end6;
94623
+ return lines3.slice(start4, keptEnd + 1).join(`
94624
+ `);
94625
+ }
94626
+ var STRUCTURED_MARKDOWN_LINE_RE2 = /^\s{0,3}(?:#{1,6}\s+\S|[-*+]\s+\S|\d+\.\s+\S|```|~~~)/m;
94627
+ var ROOT_HEADING_RE2 = /^#{1,6}\s+\S/;
94628
+ var ROOT_LIST_ITEM_RE2 = /^(?:[-*+]|\d+\.)\s+\S/;
94629
+ function looksLikeStructuredMarkdown2(input) {
94630
+ const normalized = input.replace(/\r\n?/g, `
94631
+ `).trim();
94632
+ if (!normalized)
94633
+ return false;
94634
+ return STRUCTURED_MARKDOWN_LINE_RE2.test(normalized);
94635
+ }
94636
+ function countTopLevelMarkdownRoots2(input) {
94637
+ const normalized = trimBoundaryBlankLines(input.replace(/\r\n?/g, `
94638
+ `));
94639
+ if (!normalized)
94640
+ return 0;
94641
+ const lines3 = normalized.split(`
94642
+ `);
94643
+ const commonIndent = lines3.filter((line4) => line4.trim().length > 0).reduce((min5, line4) => {
94644
+ const indent3 = line4.match(/^[ \t]*/)?.[0].length ?? 0;
94645
+ return min5 === null ? indent3 : Math.min(min5, indent3);
94646
+ }, null) ?? 0;
94647
+ let inFence = false;
94648
+ let count4 = 0;
94649
+ for (const rawLine of lines3) {
94650
+ const line4 = rawLine.slice(commonIndent);
94651
+ if (/^\s*(```|~~~)/.test(line4)) {
94652
+ inFence = !inFence;
94653
+ continue;
94654
+ }
94655
+ if (inFence)
94656
+ continue;
94657
+ const trimmed2 = line4.trim();
94658
+ if (!trimmed2)
94659
+ continue;
94660
+ if (/^[ \t]/.test(line4))
94661
+ continue;
94662
+ if (ROOT_HEADING_RE2.test(trimmed2)) {
94663
+ count4 += 1;
94664
+ continue;
94665
+ }
94666
+ if (ROOT_LIST_ITEM_RE2.test(trimmed2)) {
94667
+ count4 += 1;
94668
+ }
94669
+ }
94670
+ return count4;
94671
+ }
94672
+ function isSingleRootOutlineMarkdown(input) {
94673
+ return countTopLevelMarkdownRoots2(input) === 1;
94674
+ }
94675
+
94129
94676
  // src/services/Payload.ts
94130
94677
  import { promises as fs17 } from "node:fs";
94131
94678
  class Payload extends Tag2("Payload")() {
@@ -95084,6 +95631,33 @@ function readOptionalBoolean(obj, key) {
95084
95631
  throw invalidFieldType(key, "a boolean");
95085
95632
  return value8;
95086
95633
  }
95634
+ function expandMarkdownInputSpecs(value8) {
95635
+ return gen2(function* () {
95636
+ if (Array.isArray(value8)) {
95637
+ return yield* forEach9(value8, (item) => expandMarkdownInputSpecs(item), { concurrency: 1 });
95638
+ }
95639
+ if (!value8 || typeof value8 !== "object")
95640
+ return value8;
95641
+ const obj = value8;
95642
+ const out = {};
95643
+ for (const [key, entry] of Object.entries(obj)) {
95644
+ if (key === "markdown" && typeof entry === "string") {
95645
+ const raw4 = yield* readMarkdownTextFromInputSpec(entry);
95646
+ out[key] = trimBoundaryBlankLines(raw4);
95647
+ continue;
95648
+ }
95649
+ out[key] = yield* expandMarkdownInputSpecs(entry);
95650
+ }
95651
+ return out;
95652
+ });
95653
+ }
95654
+ function normalizeAndExpandApplyEnvelope(raw4) {
95655
+ return gen2(function* () {
95656
+ const payloadSvc = yield* Payload;
95657
+ const normalized = payloadSvc.normalizeKeys(raw4);
95658
+ return yield* expandMarkdownInputSpecs(normalized);
95659
+ });
95660
+ }
95087
95661
  function parseApplyEnvelope(raw4) {
95088
95662
  const obj = asObject(raw4);
95089
95663
  if (!obj) {
@@ -95375,6 +95949,15 @@ function normalizeTxnStatus(raw4) {
95375
95949
  function countByStatus(ops, status2) {
95376
95950
  return ops.filter((o) => String(o?.status || "") === status2).length;
95377
95951
  }
95952
+ function expectedIdMapCount(ops) {
95953
+ const seen = new Set;
95954
+ for (const op of ops) {
95955
+ const clientTempId = typeof op?.payload?.client_temp_id === "string" ? op.payload.client_temp_id.trim() : "";
95956
+ if (clientTempId)
95957
+ seen.add(clientTempId);
95958
+ }
95959
+ return seen.size;
95960
+ }
95378
95961
  function waitForTxn(params3) {
95379
95962
  return gen2(function* () {
95380
95963
  const cfg = yield* AppConfig;
@@ -95402,6 +95985,7 @@ function waitForTxn(params3) {
95402
95985
  while (true) {
95403
95986
  const inspected = yield* queue.inspect({ dbPath: cfg.storeDb, txnId: params3.txnId });
95404
95987
  const ops = Array.isArray(inspected.ops) ? inspected.ops : [];
95988
+ const idMap = Array.isArray(inspected.id_map) ? inspected.id_map : [];
95405
95989
  const txnRow = inspected.txn ?? {};
95406
95990
  const opsTotal = ops.length;
95407
95991
  const opsSucceeded = countByStatus(ops, "succeeded");
@@ -95409,6 +95993,7 @@ function waitForTxn(params3) {
95409
95993
  const opsInFlight = countByStatus(ops, "in_flight");
95410
95994
  const opsPending = countByStatus(ops, "pending");
95411
95995
  const opsFailed = ops.filter((o) => String(o?.status || "") === "pending" && Number(o?.attempts ?? 0) > 0).length;
95996
+ const expectedMappings = expectedIdMapCount(ops);
95412
95997
  const score = opsTotal > 0 ? Math.max(0, Math.min(100, Math.floor(100 * (opsSucceeded + opsDead) / opsTotal))) : 0;
95413
95998
  const status2 = normalizeTxnStatus(txnRow.status);
95414
95999
  const isDone7 = status2 === "succeeded" || status2 === "failed" || status2 === "aborted";
@@ -95417,6 +96002,34 @@ function waitForTxn(params3) {
95417
96002
  const lastUpdateAt = Number.isFinite(lastUpdateAtRaw) && lastUpdateAtRaw > 0 ? Math.floor(lastUpdateAtRaw) : 0;
95418
96003
  if (isDone7) {
95419
96004
  if (isSuccess5) {
96005
+ const mappingIncomplete = expectedMappings > 0 && idMap.length < expectedMappings;
96006
+ if (mappingIncomplete) {
96007
+ if (Date.now() < deadline) {
96008
+ yield* sleep4(millis(resolvedPollMs));
96009
+ continue;
96010
+ }
96011
+ return yield* fail8(new CliError({
96012
+ code: "TXN_TIMEOUT",
96013
+ message: `Timed out waiting for id_map to reach ${expectedMappings} entries`,
96014
+ exitCode: 1,
96015
+ details: {
96016
+ txn_id: String(txnRow.txn_id ?? params3.txnId),
96017
+ status: status2,
96018
+ expected_id_map_count: expectedMappings,
96019
+ ops_total: opsTotal,
96020
+ ops_succeeded: opsSucceeded,
96021
+ ops_dead: opsDead,
96022
+ score,
96023
+ elapsed_ms: Date.now() - startedAt,
96024
+ last_update_at: lastUpdateAt || undefined,
96025
+ id_map: idMap
96026
+ },
96027
+ hint: [
96028
+ `agent-remnote queue inspect --txn ${String(txnRow.txn_id ?? params3.txnId)}`,
96029
+ `agent-remnote queue wait --txn ${String(txnRow.txn_id ?? params3.txnId)} --timeout-ms ${resolvedTimeoutMs + resolvedPollMs}`
96030
+ ]
96031
+ }));
96032
+ }
95420
96033
  const data = {
95421
96034
  txn_id: String(txnRow.txn_id ?? params3.txnId),
95422
96035
  status: status2,
@@ -95427,9 +96040,10 @@ function waitForTxn(params3) {
95427
96040
  ops_in_flight: opsInFlight,
95428
96041
  score,
95429
96042
  is_done: true,
95430
- is_success: true,
96043
+ is_success: !mappingIncomplete,
95431
96044
  elapsed_ms: Date.now() - startedAt,
95432
- last_update_at: lastUpdateAt || undefined
96045
+ last_update_at: lastUpdateAt || undefined,
96046
+ id_map: idMap
95433
96047
  };
95434
96048
  return data;
95435
96049
  }
@@ -95448,7 +96062,8 @@ function waitForTxn(params3) {
95448
96062
  ops_pending: opsPending,
95449
96063
  score,
95450
96064
  elapsed_ms: Date.now() - startedAt,
95451
- last_update_at: lastUpdateAt || undefined
96065
+ last_update_at: lastUpdateAt || undefined,
96066
+ id_map: idMap
95452
96067
  },
95453
96068
  hint: [
95454
96069
  `agent-remnote queue inspect --txn ${String(txnRow.txn_id ?? params3.txnId)}`,
@@ -95473,7 +96088,8 @@ function waitForTxn(params3) {
95473
96088
  ops_pending: opsPending,
95474
96089
  score,
95475
96090
  elapsed_ms: Date.now() - startedAt,
95476
- last_update_at: lastUpdateAt || undefined
96091
+ last_update_at: lastUpdateAt || undefined,
96092
+ id_map: idMap
95477
96093
  },
95478
96094
  hint: [
95479
96095
  `agent-remnote queue progress --txn ${String(txnRow.txn_id ?? params3.txnId)}`,
@@ -95489,81 +96105,6 @@ function waitForTxn(params3) {
95489
96105
  });
95490
96106
  }
95491
96107
 
95492
- // src/lib/text.ts
95493
- function trimBoundaryBlankLines(input) {
95494
- const normalized = input.replace(/\r\n?/g, `
95495
- `);
95496
- const lines3 = normalized.split(`
95497
- `);
95498
- let start4 = 0;
95499
- while (start4 < lines3.length && lines3[start4]?.trim().length === 0)
95500
- start4 += 1;
95501
- if (start4 >= lines3.length)
95502
- return "";
95503
- let end6 = lines3.length - 1;
95504
- while (end6 >= start4 && lines3[end6]?.trim().length === 0)
95505
- end6 -= 1;
95506
- if (end6 < start4)
95507
- return "";
95508
- let inFence = false;
95509
- for (let i = start4;i <= end6; i += 1) {
95510
- if (/^\s*```/.test(lines3[i] ?? ""))
95511
- inFence = !inFence;
95512
- }
95513
- const keptEnd = inFence ? lines3.length - 1 : end6;
95514
- return lines3.slice(start4, keptEnd + 1).join(`
95515
- `);
95516
- }
95517
- var STRUCTURED_MARKDOWN_LINE_RE2 = /^\s{0,3}(?:#{1,6}\s+\S|[-*+]\s+\S|\d+\.\s+\S|```|~~~)/m;
95518
- var ROOT_HEADING_RE2 = /^#{1,6}\s+\S/;
95519
- var ROOT_LIST_ITEM_RE2 = /^(?:[-*+]|\d+\.)\s+\S/;
95520
- function looksLikeStructuredMarkdown2(input) {
95521
- const normalized = input.replace(/\r\n?/g, `
95522
- `).trim();
95523
- if (!normalized)
95524
- return false;
95525
- return STRUCTURED_MARKDOWN_LINE_RE2.test(normalized);
95526
- }
95527
- function countTopLevelMarkdownRoots2(input) {
95528
- const normalized = trimBoundaryBlankLines(input.replace(/\r\n?/g, `
95529
- `));
95530
- if (!normalized)
95531
- return 0;
95532
- const lines3 = normalized.split(`
95533
- `);
95534
- const commonIndent = lines3.filter((line4) => line4.trim().length > 0).reduce((min5, line4) => {
95535
- const indent3 = line4.match(/^[ \t]*/)?.[0].length ?? 0;
95536
- return min5 === null ? indent3 : Math.min(min5, indent3);
95537
- }, null) ?? 0;
95538
- let inFence = false;
95539
- let count4 = 0;
95540
- for (const rawLine of lines3) {
95541
- const line4 = rawLine.slice(commonIndent);
95542
- if (/^\s*(```|~~~)/.test(line4)) {
95543
- inFence = !inFence;
95544
- continue;
95545
- }
95546
- if (inFence)
95547
- continue;
95548
- const trimmed2 = line4.trim();
95549
- if (!trimmed2)
95550
- continue;
95551
- if (/^[ \t]/.test(line4))
95552
- continue;
95553
- if (ROOT_HEADING_RE2.test(trimmed2)) {
95554
- count4 += 1;
95555
- continue;
95556
- }
95557
- if (ROOT_LIST_ITEM_RE2.test(trimmed2)) {
95558
- count4 += 1;
95559
- }
95560
- }
95561
- return count4;
95562
- }
95563
- function isSingleRootOutlineMarkdown(input) {
95564
- return countTopLevelMarkdownRoots2(input) === 1;
95565
- }
95566
-
95567
96108
  // src/lib/hostApiUseCases.ts
95568
96109
  function clampInt3(value8, min5, max7) {
95569
96110
  if (!Number.isFinite(value8))
@@ -95735,7 +96276,9 @@ function simplifyOutlineTree(nodes) {
95735
96276
  return nodes.map((node) => ({
95736
96277
  id: node.id,
95737
96278
  depth: node.depth,
96279
+ kind: node.kind,
95738
96280
  text: node.text,
96281
+ target: node.target ?? null,
95739
96282
  references: Array.isArray(node.references) ? node.references : []
95740
96283
  }));
95741
96284
  }
@@ -96572,168 +97115,112 @@ var dbBackupsCommand = exports_Command.make("backups", { basePath, limit }, ({ b
96572
97115
  function optionToUndefined13(opt) {
96573
97116
  return isSome2(opt) ? opt.value : undefined;
96574
97117
  }
96575
- function pad2(n) {
96576
- return String(n).padStart(2, "0");
96577
- }
96578
- function msToLocalStr(ms) {
96579
- const d = new Date(ms);
96580
- return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
96581
- }
96582
- function cleanTitle(input) {
96583
- if (typeof input !== "string")
96584
- return "";
96585
- return input.split(/\s+/).join(" ").trim();
96586
- }
96587
- function asInt(value8) {
96588
- const n = typeof value8 === "number" ? value8 : typeof value8 === "string" ? Number(value8) : 0;
96589
- return Number.isFinite(n) ? n : 0;
96590
- }
96591
97118
  function clampInt4(value8, min5, max7) {
96592
97119
  if (!Number.isFinite(value8))
96593
97120
  return min5;
96594
- return Math.max(min5, Math.min(max7, value8));
97121
+ return Math.max(min5, Math.min(max7, Math.floor(value8)));
97122
+ }
97123
+ function formatCounts(counts) {
97124
+ return `total=${counts.total}, created=${counts.created}, modified_existing=${counts.modified_existing}`;
96595
97125
  }
96596
- function scalar(db, sql, params3) {
96597
- const row = db.prepare(sql).get(...params3);
96598
- return asInt(row?.c ?? row?.count ?? row?.["count(*)"]);
97126
+ function assertTimezoneInput(timezone) {
97127
+ const effective = timezone.trim();
97128
+ new Intl.DateTimeFormat("en-CA", { timeZone: effective, year: "numeric", month: "2-digit", day: "2-digit" });
96599
97129
  }
96600
97130
  var days3 = integer7("days").pipe(optional5, map34(optionToUndefined13));
96601
- var maxParents = integer7("max-parents").pipe(optional5, map34(optionToUndefined13));
96602
- var perParent = integer7("per-parent").pipe(optional5, map34(optionToUndefined13));
97131
+ var kind = choice5("kind", ["all", "created", "modified_existing"]).pipe(optional5, map34(optionToUndefined13));
97132
+ var aggregate2 = choice5("aggregate", ["day", "parent"]).pipe(repeated5);
97133
+ var timezone = text9("timezone").pipe(optional5, map34(optionToUndefined13));
97134
+ var itemLimit = integer7("item-limit").pipe(optional5, map34(optionToUndefined13));
97135
+ var aggregateLimit = integer7("aggregate-limit").pipe(optional5, map34(optionToUndefined13));
96603
97136
  var dbRecentCommand = exports_Command.make("recent", {
96604
97137
  days: days3,
96605
- maxParents,
96606
- perParent,
96607
- noParentGroup: boolean8("no-parent-group")
96608
- }, ({ days: days4, maxParents: maxParents2, perParent: perParent2, noParentGroup }) => gen2(function* () {
96609
- const d = clampInt4(days4 ?? 15, 1, 3650);
96610
- const maxP = clampInt4(maxParents2 ?? 20, 1, 500);
96611
- const perP = clampInt4(perParent2 ?? 10, 1, 500);
96612
- const cutoffMs = Date.now() - d * 86400 * 1000;
97138
+ kind,
97139
+ aggregate: aggregate2,
97140
+ timezone,
97141
+ itemLimit,
97142
+ aggregateLimit
97143
+ }, ({ days: days4, kind: kind2, aggregate: aggregate3, timezone: timezone2, itemLimit: itemLimit2, aggregateLimit: aggregateLimit2 }) => gen2(function* () {
96613
97144
  const cfg = yield* AppConfig;
96614
97145
  const remDb = yield* RemDb;
96615
- const result = yield* remDb.withDb(cfg.remnoteDb, (db) => {
96616
- const counts = {
96617
- quanta_created: scalar(db, "select count(*) as c from quanta where json_extract(doc,'$.createdAt') >= ?", [
96618
- cutoffMs
96619
- ]),
96620
- quanta_touched: scalar(db, "select count(*) as c from quanta where json_extract(doc,'$.m') >= ?", [cutoffMs]),
96621
- rem_created: scalar(db, `
96622
- select count(*) as c
96623
- from quanta q
96624
- join remsSearchInfos r on r.id=q._id
96625
- where json_extract(q.doc,'$.createdAt') >= ?
96626
- `, [cutoffMs]),
96627
- rem_created_and_edited: scalar(db, `
96628
- select count(*) as c
96629
- from quanta q
96630
- join remsSearchInfos r on r.id=q._id
96631
- where json_extract(q.doc,'$.createdAt') >= ?
96632
- and json_extract(q.doc,'$.m') > json_extract(q.doc,'$.createdAt')
96633
- `, [cutoffMs]),
96634
- rem_modified_old: scalar(db, `
96635
- select count(*) as c
96636
- from quanta q
96637
- join remsSearchInfos r on r.id=q._id
96638
- where json_extract(q.doc,'$.m') >= ?
96639
- and json_extract(q.doc,'$.createdAt') < ?
96640
- `, [cutoffMs, cutoffMs])
96641
- };
96642
- const rows = db.prepare(`
96643
- select
96644
- q._id as id,
96645
- cast(json_extract(q.doc,'$.createdAt') as integer) as createdAt,
96646
- cast(json_extract(q.doc,'$.m') as integer) as updatedAt,
96647
- json_extract(r.doc,'$.r') as preview,
96648
- json_extract(q.doc,'$.parent') as parentId,
96649
- json_extract(pr.doc,'$.r') as parentPreview
96650
- from quanta q
96651
- join remsSearchInfos r on r.id=q._id
96652
- left join remsSearchInfos pr on pr.id = json_extract(q.doc,'$.parent')
96653
- where cast(json_extract(q.doc,'$.createdAt') as integer) >= ?
96654
- order by createdAt desc
96655
- `).all(cutoffMs);
96656
- const items = rows.map((row) => {
96657
- const createdAt = asInt(row.createdAt);
96658
- const updatedAt = asInt(row.updatedAt);
96659
- const preview = cleanTitle(row.preview) || "<no preview>";
96660
- const parentId = typeof row.parentId === "string" ? row.parentId : row.parentId ? String(row.parentId) : "";
96661
- const parentPreview = cleanTitle(row.parentPreview) || "<unknown parent>";
96662
- return {
96663
- id: String(row.id),
96664
- created_at: createdAt,
96665
- updated_at: updatedAt,
96666
- preview,
96667
- parent_id: parentId,
96668
- parent_preview: parentPreview,
96669
- edited_after_create: createdAt > 0 && updatedAt > createdAt
96670
- };
97146
+ if (cfg.apiBaseUrl) {
97147
+ return yield* fail8(new CliError({
97148
+ code: "INVALID_ARGS",
97149
+ message: "db recent is local-only when apiBaseUrl is configured",
97150
+ exitCode: 2,
97151
+ details: { apiBaseUrl: cfg.apiBaseUrl }
97152
+ }));
97153
+ }
97154
+ const effectiveDays = clampInt4(days4 ?? 15, 1, 3650);
97155
+ const effectiveKind = kind2 ?? "all";
97156
+ const normalizedTimezone = timezone2?.trim();
97157
+ const effectiveItemLimit = clampInt4(itemLimit2 ?? 50, 1, 500);
97158
+ const effectiveAggregateLimit = clampInt4(aggregateLimit2 ?? 20, 1, 200);
97159
+ const aggregateDimensions = Array.from(new Set(aggregate3));
97160
+ if (timezone2 !== undefined) {
97161
+ const timezoneCheck = try_3({
97162
+ try: () => assertTimezoneInput(normalizedTimezone ?? ""),
97163
+ catch: () => new CliError({
97164
+ code: "INVALID_ARGS",
97165
+ message: `Invalid timezone: ${timezone2}`,
97166
+ exitCode: 2,
97167
+ details: { timezone: timezone2 }
97168
+ })
96671
97169
  });
96672
- return { counts, items };
96673
- });
96674
- const dbPath = result.info.dbPath;
96675
- const cutoffText = msToLocalStr(cutoffMs);
96676
- const header = [
96677
- `# RemNote Overview (last ${d} days)`,
96678
- ``,
96679
- `- db: \`${dbPath}\``,
96680
- `- cutoff: \`${cutoffText}\``,
96681
- `- rem (index): created \`${result.result.counts.rem_created}\`; created+edited \`${result.result.counts.rem_created_and_edited}\`; edited existing \`${result.result.counts.rem_modified_old}\``,
96682
- `- quanta (raw): createdAt>=cutoff \`${result.result.counts.quanta_created}\`; m>=cutoff \`${result.result.counts.quanta_touched}\``,
96683
- ``
96684
- ].join(`
96685
- `);
96686
- const ids3 = [];
96687
- const mdLines = [header.trimEnd()];
96688
- if (result.result.items.length > 0) {
96689
- if (noParentGroup) {
96690
- mdLines.push("## New Rems (newest first)", "");
96691
- const take10 = Math.min(result.result.items.length, maxP * perP);
96692
- for (const row of result.result.items.slice(0, take10)) {
96693
- ids3.push(row.id);
96694
- const marker = row.edited_after_create ? " (edited after creation)" : "";
96695
- mdLines.push(`- \`${msToLocalStr(row.created_at)}\` ${row.preview} \`${row.id}\`${marker}`);
96696
- }
96697
- } else {
96698
- const groups = new Map;
96699
- for (const row of result.result.items) {
96700
- const key = row.parent_id;
96701
- const existing = groups.get(key);
96702
- if (existing) {
96703
- existing.items.push(row);
96704
- } else {
96705
- groups.set(key, { parent_id: key, parent_title: row.parent_preview, items: [row] });
96706
- }
96707
- }
96708
- const ordered = Array.from(groups.values()).sort((a, b) => {
96709
- const aMax = Math.max(...a.items.map((r) => r.created_at));
96710
- const bMax = Math.max(...b.items.map((r) => r.created_at));
96711
- return bMax - aMax;
96712
- }).slice(0, maxP);
96713
- mdLines.push(`## New Rems (grouped by parent, up to ${maxP} groups × ${perP} each)`, "");
96714
- for (const group4 of ordered) {
96715
- const items = [...group4.items].sort((a, b) => b.created_at - a.created_at);
96716
- mdLines.push(`### ${group4.parent_title} (${group4.items.length})`, "");
96717
- for (const row of items.slice(0, perP)) {
96718
- ids3.push(row.id);
96719
- const marker = row.edited_after_create ? " (edited after creation)" : "";
96720
- mdLines.push(`- \`${msToLocalStr(row.created_at)}\` ${row.preview} \`${row.id}\`${marker}`);
96721
- }
96722
- mdLines.push("");
96723
- }
96724
- }
97170
+ yield* timezoneCheck;
96725
97171
  }
97172
+ const effectiveTimezone = normalizedTimezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC";
97173
+ const result = yield* remDb.withDb(cfg.remnoteDb, (db) => executeSummarizeRecentActivity(db, {
97174
+ days: effectiveDays,
97175
+ kind: effectiveKind,
97176
+ aggregates: aggregateDimensions,
97177
+ timezone: effectiveTimezone,
97178
+ itemLimit: effectiveItemLimit,
97179
+ aggregateLimit: effectiveAggregateLimit
97180
+ }));
96726
97181
  const data = {
96727
- db_path: dbPath,
97182
+ db_path: result.info.dbPath,
96728
97183
  resolution: result.info.source,
96729
- days: d,
96730
- cutoff_ms: cutoffMs,
97184
+ days: result.result.days,
97185
+ timezone: result.result.timezone,
97186
+ kind: effectiveKind,
97187
+ aggregate_dimensions: aggregateDimensions,
97188
+ item_limit: effectiveItemLimit,
97189
+ aggregate_limit: effectiveAggregateLimit,
97190
+ cutoff_ms: result.result.cutoff_ms,
96731
97191
  counts: result.result.counts,
96732
- items: result.result.items
97192
+ items: result.result.items,
97193
+ aggregates: result.result.aggregates
96733
97194
  };
96734
- yield* writeSuccess({ data, ids: ids3, md: mdLines.join(`
97195
+ const mdLines = [
97196
+ `# Recent Activity (${effectiveDays}d)`,
97197
+ "",
97198
+ `- db: \`${result.info.dbPath}\``,
97199
+ `- timezone: \`${result.result.timezone}\``,
97200
+ `- kind: \`${effectiveKind}\``,
97201
+ `- counts: ${formatCounts(result.result.counts)}`,
97202
+ ""
97203
+ ];
97204
+ if (result.result.items.length > 0) {
97205
+ mdLines.push("## Items", "");
97206
+ for (const item of result.result.items) {
97207
+ mdLines.push(`- [${item.activity_kind}] ${item.preview} \`${item.id}\``);
97208
+ }
97209
+ mdLines.push("");
97210
+ }
97211
+ if (result.result.aggregates.length > 0) {
97212
+ mdLines.push("## Aggregates", "");
97213
+ for (const entry of result.result.aggregates) {
97214
+ mdLines.push(`- ${entry.dimension}:${entry.key} (${formatCounts(entry.counts)})`);
97215
+ }
97216
+ }
97217
+ yield* writeSuccess({
97218
+ data,
97219
+ ids: result.result.items.map((item) => item.id),
97220
+ md: mdLines.join(`
96735
97221
  `).trimEnd() + `
96736
- ` });
97222
+ `
97223
+ });
96737
97224
  }).pipe(catchAll2(writeFailure)));
96738
97225
 
96739
97226
  // src/commands/read/db/index.ts
@@ -96764,7 +97251,7 @@ var backupCleanupCommand = exports_Command.make("cleanup", {
96764
97251
  backupRemId,
96765
97252
  maxDeleteSubtreeNodes,
96766
97253
  state,
96767
- kind,
97254
+ kind: kind2,
96768
97255
  olderThanHours,
96769
97256
  limit: limit2,
96770
97257
  wait: wait2,
@@ -96819,7 +97306,7 @@ var backupCleanupCommand = exports_Command.make("cleanup", {
96819
97306
  const states = state.length > 0 ? state : backupRemId ? [] : ["orphan"];
96820
97307
  const items = listBackupArtifacts(db, {
96821
97308
  states,
96822
- kinds: kind,
97309
+ kinds: kind2,
96823
97310
  backupRemId,
96824
97311
  olderThanHours,
96825
97312
  limit: limit2
@@ -96894,7 +97381,7 @@ var backupListCommand = exports_Command.make("list", {
96894
97381
  kind: choice5("kind", ["children_replace", "selection_replace"]).pipe(repeated5),
96895
97382
  olderThanHours: integer7("older-than-hours").pipe(optional5, map34(optionToUndefined15)),
96896
97383
  limit: integer7("limit").pipe(withDefault5(100))
96897
- }, ({ state, kind, olderThanHours, limit: limit2 }) => gen2(function* () {
97384
+ }, ({ state, kind: kind2, olderThanHours, limit: limit2 }) => gen2(function* () {
96898
97385
  if (olderThanHours !== undefined && olderThanHours < 0) {
96899
97386
  return yield* fail8(new CliError({
96900
97387
  code: "INVALID_ARGS",
@@ -96918,7 +97405,7 @@ var backupListCommand = exports_Command.make("list", {
96918
97405
  try {
96919
97406
  const items = listBackupArtifacts(db, {
96920
97407
  states: state,
96921
- kinds: kind,
97408
+ kinds: kind2,
96922
97409
  olderThanHours,
96923
97410
  limit: limit2
96924
97411
  });
@@ -97073,8 +97560,9 @@ var applyCommand = exports_Command.make("apply", {
97073
97560
  const hostApi = yield* HostApiClient;
97074
97561
  const payloadSvc = yield* Payload;
97075
97562
  const raw4 = yield* payloadSvc.readJson(payload);
97563
+ const expanded = yield* normalizeAndExpandApplyEnvelope(raw4);
97076
97564
  const parsed = yield* try_3({
97077
- try: () => parseApplyEnvelope(payloadSvc.normalizeKeys(raw4)),
97565
+ try: () => parseApplyEnvelope(expanded),
97078
97566
  catch: (error4) => isCliError(error4) ? error4 : new CliError({
97079
97567
  code: "INVALID_PAYLOAD",
97080
97568
  message: String(error4?.message || "Invalid apply envelope"),
@@ -97106,7 +97594,7 @@ var applyCommand = exports_Command.make("apply", {
97106
97594
  const data = cfg.apiBaseUrl ? yield* hostApi.writeApply({
97107
97595
  baseUrl: cfg.apiBaseUrl,
97108
97596
  body: {
97109
- ...raw4,
97597
+ ...expanded,
97110
97598
  priority: resolvedPriority,
97111
97599
  clientId: resolvedClientId,
97112
97600
  idempotencyKey: resolvedIdempotencyKey,
@@ -97121,7 +97609,7 @@ var applyCommand = exports_Command.make("apply", {
97121
97609
  yield* validateOptionMutationOps({ scopeLabel: "generic", ops: compiled.ops });
97122
97610
  return yield* executeWriteApplyUseCase({
97123
97611
  raw: {
97124
- ...raw4,
97612
+ ...expanded,
97125
97613
  priority: resolvedPriority,
97126
97614
  clientId: resolvedClientId,
97127
97615
  idempotencyKey: resolvedIdempotencyKey,
@@ -97134,7 +97622,7 @@ var applyCommand = exports_Command.make("apply", {
97134
97622
  pollMs: pollMs3
97135
97623
  });
97136
97624
  });
97137
- const out = compiled.kind === "actions" ? { ...data, alias_map: compiled.aliasMap } : data;
97625
+ const out = compiled.kind === "actions" ? { ...data, alias_map: data?.alias_map ?? compiled.aliasMap } : data;
97138
97626
  yield* writeSuccess({
97139
97627
  data: out,
97140
97628
  ids: [data.txn_id, ...data.op_ids],
@@ -98502,7 +98990,7 @@ var readSelectionSnapshotCommand = exports_Command.make("snapshot", { stateFile:
98502
98990
  const hostApi = yield* HostApiClient;
98503
98991
  const snapshot2 = cfg.apiBaseUrl ? yield* hostApi.selectionSnapshot({ baseUrl: cfg.apiBaseUrl, stateFile: stateFile13, staleMs: staleMs6 }) : loadBridgeSelectionSnapshot({ stateFile: stateFile13, staleMs: staleMs6 });
98504
98992
  const selection = snapshot2.selection;
98505
- const kind = selection?.kind ?? "none";
98993
+ const kind2 = selection?.kind ?? "none";
98506
98994
  const selectionType = selection && "selectionType" in selection ? selection.selectionType ?? "" : "";
98507
98995
  let selected = 0;
98508
98996
  let roots2 = 0;
@@ -98522,14 +99010,14 @@ var readSelectionSnapshotCommand = exports_Command.make("snapshot", { stateFile:
98522
99010
  }
98523
99011
  const md = [
98524
99012
  `- status: ${snapshot2.status}`,
98525
- `- kind: ${kind}`,
99013
+ `- kind: ${kind2}`,
98526
99014
  `- selected: ${selected}`,
98527
99015
  `- roots: ${roots2}`,
98528
99016
  selectionType ? `- selection_type: ${selectionType}` : "",
98529
- kind === "rem" ? `- truncated: ${truncated ? "true" : "false"}` : "",
98530
- kind === "text" ? `- rem_id: ${textRemId}` : "",
98531
- kind === "text" ? `- range: ${textRange}` : "",
98532
- kind === "text" ? `- is_reverse: ${textIsReverse ? "true" : "false"}` : "",
99017
+ kind2 === "rem" ? `- truncated: ${truncated ? "true" : "false"}` : "",
99018
+ kind2 === "text" ? `- rem_id: ${textRemId}` : "",
99019
+ kind2 === "text" ? `- range: ${textRange}` : "",
99020
+ kind2 === "text" ? `- is_reverse: ${textIsReverse ? "true" : "false"}` : "",
98533
99021
  `- clients: ${snapshot2.clients}`,
98534
99022
  `- state_file: ${snapshot2.state_file}`,
98535
99023
  `- updated_at: ${snapshot2.updatedAt || ""}`,
@@ -98654,7 +99142,7 @@ var readUiContextDescribeCommand = exports_Command.make("describe", { stateFile:
98654
99142
  const pageRemId = normalizeText4(ui?.pageRemId);
98655
99143
  const focusedPortalId = normalizeText4(ui?.focusedPortalId);
98656
99144
  const focusedRemId = normalizeText4(ui?.focusedRemId);
98657
- const kind = computePortalKind2(pageRemId, focusedPortalId);
99145
+ const kind2 = computePortalKind2(pageRemId, focusedPortalId);
98658
99146
  const selectionKind = selection?.kind ?? "none";
98659
99147
  const selectionIds = uniqueNonEmpty2(selection?.kind === "rem" ? selection.remIds.map(String) : []);
98660
99148
  const selectionTotalCountRaw = selection?.kind === "rem" ? Number(selection.totalCount ?? 0) : selection?.kind === "text" ? 1 : 0;
@@ -98690,12 +99178,12 @@ var readUiContextDescribeCommand = exports_Command.make("describe", { stateFile:
98690
99178
  return mapOrError;
98691
99179
  });
98692
99180
  const pageTitle = pageRemId ? titles.get(pageRemId) || "" : "";
98693
- const portalEffectiveId = kind === "page" ? pageRemId || focusedPortalId : focusedPortalId;
98694
- const portalTitle = kind === "page" ? pageRemId ? titles.get(pageRemId) || "" : "" : focusedPortalId ? titles.get(focusedPortalId) || "" : "";
99181
+ const portalEffectiveId = kind2 === "page" ? pageRemId || focusedPortalId : focusedPortalId;
99182
+ const portalTitle = kind2 === "page" ? pageRemId ? titles.get(pageRemId) || "" : "" : focusedPortalId ? titles.get(focusedPortalId) || "" : "";
98695
99183
  const portalContent = (() => {
98696
99184
  const parts2 = [];
98697
- if (kind !== "unknown")
98698
- parts2.push(`(${kind})`);
99185
+ if (kind2 !== "unknown")
99186
+ parts2.push(`(${kind2})`);
98699
99187
  if (portalTitle)
98700
99188
  parts2.push(portalTitle);
98701
99189
  const idToken = formatIdToken(portalEffectiveId);
@@ -98779,7 +99267,7 @@ var readUiContextDescribeCommand = exports_Command.make("describe", { stateFile:
98779
99267
  title: anchorTitle || undefined
98780
99268
  },
98781
99269
  portal: {
98782
- kind,
99270
+ kind: kind2,
98783
99271
  id: portalEffectiveId,
98784
99272
  title: portalTitle || undefined
98785
99273
  },
@@ -100533,8 +101021,8 @@ function compileTableValueOps(params3) {
100533
101021
  const item = params3.values[i];
100534
101022
  const propertyId = resolvePropertyId(item, params3.properties);
100535
101023
  const prop = resolvePropertyDef(propertyId, params3.properties);
100536
- const kind = prop?.kind ?? "unknown";
100537
- switch (kind) {
101024
+ const kind2 = prop?.kind ?? "unknown";
101025
+ switch (kind2) {
100538
101026
  case "select": {
100539
101027
  const ids3 = resolveOptionIds(coerceSelectInput(item.value), prop);
100540
101028
  const payload = {
@@ -102753,12 +103241,16 @@ var writeRemCreateCommand = exports_Command.make("create", {
102753
103241
  });
102754
103242
  const waited = wait3 ? yield* waitForTxn({ txnId: data.txn_id, timeoutMs: timeoutMs4, pollMs: pollMs3 }) : null;
102755
103243
  const queue = yield* Queue;
102756
- const created = waited && waited.is_success === true ? yield* queue.inspect({ dbPath: cfg.storeDb, txnId: data.txn_id }).pipe(map17((inspected) => {
102757
- const idMap = Array.isArray(inspected?.id_map) ? inspected.id_map : [];
103244
+ const created = waited && waited.is_success === true ? yield* gen2(function* () {
103245
+ let idMap = Array.isArray(waited?.id_map) ? waited.id_map : [];
103246
+ if (idMap.length === 0) {
103247
+ const inspected = yield* queue.inspect({ dbPath: cfg.storeDb, txnId: data.txn_id }).pipe(catchAll2(() => succeed8({ id_map: [] })));
103248
+ idMap = Array.isArray(inspected?.id_map) ? inspected.id_map : [];
103249
+ }
102758
103250
  const match24 = idMap.find((r) => String(r?.client_temp_id ?? "") === remClientTempId);
102759
103251
  const remoteId = match24?.remote_id ? String(match24.remote_id) : "";
102760
- return remoteId ? { rem_id: remoteId } : {};
102761
- }), catchAll2(() => succeed8({}))) : {};
103252
+ return remoteId ? { rem_id: remoteId, id_map: idMap } : { id_map: idMap };
103253
+ }) : {};
102762
103254
  const out = waited ? { ...data, ...waited, rem_client_temp_id: remClientTempId, ...created } : { ...data, rem_client_temp_id: remClientTempId };
102763
103255
  yield* writeSuccess({
102764
103256
  data: out,
@@ -102828,6 +103320,24 @@ function buildActionEnvelope(params3) {
102828
103320
  });
102829
103321
  }
102830
103322
  function resolveCurrentSelectionRemId(params3) {
103323
+ return gen2(function* () {
103324
+ const resolved = yield* resolveCurrentSelectionRemIds(params3);
103325
+ if (resolved.rem_ids.length !== 1) {
103326
+ return yield* fail8(new CliError({
103327
+ code: "INVALID_ARGS",
103328
+ message: "Current selection must resolve to exactly one selected Rem",
103329
+ exitCode: 2,
103330
+ details: { total_count: resolved.rem_ids.length, selection: resolved.selection }
103331
+ }));
103332
+ }
103333
+ return {
103334
+ source: "selection",
103335
+ rem_id: resolved.rem_ids[0],
103336
+ selection: resolved.selection
103337
+ };
103338
+ });
103339
+ }
103340
+ function resolveCurrentSelectionRemIds(params3) {
102831
103341
  return gen2(function* () {
102832
103342
  const cfg = yield* AppConfig;
102833
103343
  const hostApi = yield* HostApiClient;
@@ -102835,18 +103345,20 @@ function resolveCurrentSelectionRemId(params3) {
102835
103345
  const totalCountRaw = Number(data?.total_count ?? 0);
102836
103346
  const totalCount = Number.isFinite(totalCountRaw) && totalCountRaw >= 0 ? Math.floor(totalCountRaw) : 0;
102837
103347
  const truncated = data?.truncated === true;
102838
- const currentId = typeof data?.current?.id === "string" ? data.current.id.trim() : Array.isArray(data?.ids) ? String(data.ids[0] ?? "").trim() : "";
102839
- if (truncated || totalCount !== 1 || !currentId) {
103348
+ const listedIds = Array.isArray(data?.ids) ? data.ids.filter((value8) => typeof value8 === "string").map((value8) => value8.trim()).filter((value8) => value8.length > 0) : [];
103349
+ const currentId = typeof data?.current?.id === "string" && data.current.id.trim() ? data.current.id.trim() : "";
103350
+ const ids4 = listedIds.length > 0 ? listedIds : currentId ? [currentId] : [];
103351
+ if (truncated || totalCount < 1 || ids4.length === 0 || ids4.length !== totalCount) {
102840
103352
  return yield* fail8(new CliError({
102841
103353
  code: "INVALID_ARGS",
102842
- message: "Current selection must resolve to exactly one selected Rem",
103354
+ message: "Current selection must resolve to one or more selected Rems",
102843
103355
  exitCode: 2,
102844
- details: { total_count: totalCount, truncated, current_id: currentId || null, selection: data }
103356
+ details: { total_count: totalCount, truncated, ids: ids4, selection: data }
102845
103357
  }));
102846
103358
  }
102847
103359
  return {
102848
103360
  source: "selection",
102849
- rem_id: currentId,
103361
+ rem_ids: ids4,
102850
103362
  selection: data
102851
103363
  };
102852
103364
  });
@@ -103446,6 +103958,117 @@ var writeRemMoveCommand = exports_Command.make("move", {
103446
103958
  });
103447
103959
  }).pipe(catchAll2(writeFailure)));
103448
103960
 
103961
+ // src/commands/write/rem/replace.ts
103962
+ var writeRemReplaceCommand = exports_Command.make("replace", {
103963
+ rem: text9("rem").pipe(repeated5),
103964
+ selection: boolean8("selection"),
103965
+ stateFile: text9("state-file").pipe(optional5, map34(optionToUndefined43)),
103966
+ staleMs: integer7("stale-ms").pipe(optional5, map34(optionToUndefined43)),
103967
+ surface: choice5("surface", ["children", "self"]),
103968
+ markdown: text9("markdown"),
103969
+ assert: choice5("assert", ["single-root", "preserve-anchor", "no-literal-bullet"]).pipe(repeated5),
103970
+ notify: writeCommonOptions.notify,
103971
+ ensureDaemon: writeCommonOptions.ensureDaemon,
103972
+ wait: writeCommonOptions.wait,
103973
+ timeoutMs: writeCommonOptions.timeoutMs,
103974
+ pollMs: writeCommonOptions.pollMs,
103975
+ dryRun: writeCommonOptions.dryRun,
103976
+ priority: writeCommonOptions.priority,
103977
+ clientId: writeCommonOptions.clientId,
103978
+ idempotencyKey: writeCommonOptions.idempotencyKey,
103979
+ meta: writeCommonOptions.meta
103980
+ }, ({
103981
+ rem,
103982
+ selection,
103983
+ stateFile: stateFile24,
103984
+ staleMs: staleMs11,
103985
+ surface,
103986
+ markdown: markdown2,
103987
+ assert: assert2,
103988
+ notify: notify3,
103989
+ ensureDaemon: ensureDaemon5,
103990
+ wait: wait3,
103991
+ timeoutMs: timeoutMs4,
103992
+ pollMs: pollMs3,
103993
+ dryRun,
103994
+ priority: priority3,
103995
+ clientId: clientId3,
103996
+ idempotencyKey: idempotencyKey3,
103997
+ meta
103998
+ }) => gen2(function* () {
103999
+ yield* ensureWaitArgs({ wait: wait3, timeoutMs: timeoutMs4, pollMs: pollMs3, dryRun });
104000
+ const explicitIds = rem.map(normalizeRemIdInput3).filter(Boolean);
104001
+ if (explicitIds.length === 0 && !selection || explicitIds.length > 0 && selection) {
104002
+ return yield* fail8(new CliError({
104003
+ code: "INVALID_ARGS",
104004
+ message: "Provide exactly one target selector via repeated --rem or --selection",
104005
+ exitCode: 2
104006
+ }));
104007
+ }
104008
+ const target2 = explicitIds.length > 0 ? {
104009
+ source: "explicit",
104010
+ rem_ids: Array.from(new Set(explicitIds))
104011
+ } : yield* resolveCurrentSelectionRemIds({ stateFile: stateFile24, staleMs: staleMs11 });
104012
+ if (surface === "children" && target2.rem_ids.length !== 1) {
104013
+ return yield* fail8(new CliError({
104014
+ code: "INVALID_ARGS",
104015
+ message: "rem replace --surface children requires exactly one target Rem",
104016
+ exitCode: 2,
104017
+ details: { surface, target: target2 }
104018
+ }));
104019
+ }
104020
+ const assertions = assert2;
104021
+ if (surface === "self" && assertions.includes("preserve-anchor")) {
104022
+ return yield* fail8(new CliError({
104023
+ code: "INVALID_ARGS",
104024
+ message: "rem replace --surface=self does not support --assert preserve-anchor",
104025
+ exitCode: 2
104026
+ }));
104027
+ }
104028
+ const markdownValue = yield* readMarkdownArg(markdown2);
104029
+ const body = yield* buildActionEnvelope({
104030
+ action: "rem.replace",
104031
+ remId: target2.rem_ids[0] ?? "",
104032
+ input: {
104033
+ surface,
104034
+ rem_ids: target2.rem_ids,
104035
+ markdown: markdownValue,
104036
+ ...assertions.length > 0 ? { assertions } : {}
104037
+ },
104038
+ priority: priority3,
104039
+ clientId: clientId3,
104040
+ idempotencyKey: idempotencyKey3,
104041
+ metaSpec: meta,
104042
+ notify: notify3,
104043
+ ensureDaemon: ensureDaemon5
104044
+ });
104045
+ if (dryRun) {
104046
+ const compiled = yield* dryRunEnvelope(body);
104047
+ yield* writeSuccess({
104048
+ data: { dry_run: true, target: { source: target2.source, rem_ids: target2.rem_ids }, ...compiled },
104049
+ md: `- dry_run: true
104050
+ - action: rem.replace
104051
+ - surface: ${surface}
104052
+ - targets: ${target2.rem_ids.length}
104053
+ `
104054
+ });
104055
+ return;
104056
+ }
104057
+ const data = yield* submitActionEnvelope({ body, wait: wait3, timeoutMs: timeoutMs4, pollMs: pollMs3 });
104058
+ yield* writeSuccess({
104059
+ data,
104060
+ ids: [data.txn_id, ...Array.isArray(data.op_ids) ? data.op_ids : []],
104061
+ md: [
104062
+ `- txn_id: ${data.txn_id}`,
104063
+ `- op_ids: ${Array.isArray(data.op_ids) ? data.op_ids.length : ""}`,
104064
+ `- notified: ${data.notified}`,
104065
+ `- sent: ${data.sent ?? ""}`,
104066
+ ...data.status ? [`- status: ${data.status}`, `- elapsed_ms: ${data.elapsed_ms ?? ""}`] : []
104067
+ ].join(`
104068
+ `)
104069
+ });
104070
+ }).pipe(catchAll2(writeFailure)));
104071
+
103449
104072
  // src/commands/write/tag/index.ts
103450
104073
  function normalizeRemIdInput5(raw4) {
103451
104074
  const trimmed2 = raw4.trim();
@@ -103719,6 +104342,7 @@ var writeRemSetTextCommand = exports_Command.make("set-text", {
103719
104342
 
103720
104343
  // src/commands/rem/index.ts
103721
104344
  var remCommand = exports_Command.make("rem", {}).pipe(exports_Command.withSubcommands([
104345
+ writeRemReplaceCommand,
103722
104346
  writeRemChildrenCommand,
103723
104347
  writeRemCreateCommand,
103724
104348
  writeRemMoveCommand,
@@ -104058,7 +104682,7 @@ var replaceMarkdownCommand = exports_Command.make("markdown", {
104058
104682
  ].join(`
104059
104683
  `)
104060
104684
  });
104061
- }).pipe(catchAll2(writeFailure))).pipe(exports_Command.withDescription("advanced/local-only block replace command; canonical anchor-preserving rewrites should prefer rem children replace"));
104685
+ }).pipe(catchAll2(writeFailure))).pipe(exports_Command.withDescription("advanced/local-only block replace command; canonical replace workflows should prefer rem replace"));
104062
104686
 
104063
104687
  // src/commands/write/replace/text.ts
104064
104688
  function optionToUndefined56(opt) {
@@ -106310,12 +106934,16 @@ var writePortalCreateCommand = exports_Command.make("create", {
106310
106934
  });
106311
106935
  const waited = wait5 ? yield* waitForTxn({ txnId: data.txn_id, timeoutMs: timeoutMs7, pollMs: pollMs5 }) : null;
106312
106936
  const queue = yield* Queue;
106313
- const created = waited && waited.is_success === true ? yield* queue.inspect({ dbPath: cfg.storeDb, txnId: data.txn_id }).pipe(map17((inspected) => {
106314
- const idMap = Array.isArray(inspected?.id_map) ? inspected.id_map : [];
106937
+ const created = waited && waited.is_success === true ? yield* gen2(function* () {
106938
+ let idMap = Array.isArray(waited?.id_map) ? waited.id_map : [];
106939
+ if (idMap.length === 0) {
106940
+ const inspected = yield* queue.inspect({ dbPath: cfg.storeDb, txnId: data.txn_id }).pipe(catchAll2(() => succeed8({ id_map: [] })));
106941
+ idMap = Array.isArray(inspected?.id_map) ? inspected.id_map : [];
106942
+ }
106315
106943
  const match24 = idMap.find((r) => String(r?.client_temp_id ?? "") === portalClientTempId);
106316
106944
  const remoteId = match24?.remote_id ? String(match24.remote_id) : "";
106317
- return remoteId ? { portal_rem_id: remoteId } : {};
106318
- }), catchAll2(() => succeed8({}))) : {};
106945
+ return remoteId ? { portal_rem_id: remoteId, id_map: idMap } : { id_map: idMap };
106946
+ }) : {};
106319
106947
  const out = waited ? { ...data, ...waited, portal_client_temp_id: portalClientTempId, ...created } : { ...data, portal_client_temp_id: portalClientTempId };
106320
106948
  yield* writeSuccess({
106321
106949
  data: out,