signetai 0.164.5 → 0.166.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.
package/dist/mcp-stdio.js CHANGED
@@ -43925,6 +43925,8 @@ function buildRememberRequestBody(content, options = {}) {
43925
43925
  validFrom: options.validFrom,
43926
43926
  validUntil: options.validUntil,
43927
43927
  sourceCreatedAt: options.sourceCreatedAt,
43928
+ reviewAfter: options.reviewAfter,
43929
+ supersedes: options.supersedes,
43928
43930
  hints: options.hints,
43929
43931
  transcript: options.transcript,
43930
43932
  structured: normalizeStructuredMemoryPayload(options.structured),
@@ -47358,6 +47360,15 @@ function up105(db) {
47358
47360
  INSERT INTO entities_fts(entities_fts) VALUES ('rebuild');
47359
47361
  `);
47360
47362
  }
47363
+ function hasColumn17(db, table, column) {
47364
+ return db.prepare(`PRAGMA table_info(${table})`).all().some((row) => row.name === column);
47365
+ }
47366
+ function up106(db) {
47367
+ if (!hasColumn17(db, "memories", "review_after")) {
47368
+ db.exec("ALTER TABLE memories ADD COLUMN review_after TEXT;");
47369
+ }
47370
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_review_after ON memories(review_after);");
47371
+ }
47361
47372
  var MIGRATIONS = [
47362
47373
  {
47363
47374
  version: 1,
@@ -48190,6 +48201,14 @@ var MIGRATIONS = [
48190
48201
  version: 105,
48191
48202
  name: "agent-scoped-entity-name",
48192
48203
  up: up105
48204
+ },
48205
+ {
48206
+ version: 106,
48207
+ name: "memory-review-after",
48208
+ up: up106,
48209
+ artifacts: {
48210
+ columns: [{ table: "memories", column: "review_after" }]
48211
+ }
48193
48212
  }
48194
48213
  ];
48195
48214
  var LATEST_SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;
@@ -48271,6 +48290,7 @@ var ONTOLOGY_PROPOSAL_OPERATIONS = [
48271
48290
  "update_link",
48272
48291
  "archive_link",
48273
48292
  "merge_entities",
48293
+ "merge_aspects",
48274
48294
  "supersede_claim_value",
48275
48295
  "create_policy",
48276
48296
  "create_action_type",
@@ -50033,8 +50053,10 @@ var DEFAULT_PIPELINE_V2 = {
50033
50053
  traversal: {
50034
50054
  enabled: true,
50035
50055
  primary: true,
50036
- maxAspectsPerEntity: 10,
50037
- maxAttributesPerAspect: 20,
50056
+ maxAspectsPerEntity: 20,
50057
+ maxAttributesPerAspect: 50,
50058
+ maxWriteAspectsPerEntity: 20,
50059
+ maxWriteAttributesPerAspect: 50,
50038
50060
  maxDependencyHops: 10,
50039
50061
  minDependencyStrength: 0.3,
50040
50062
  maxBranching: 4,
@@ -50699,8 +50721,8 @@ function txIngestEnvelope(db, mem) {
50699
50721
  importance, type, tags, pinned, is_deleted, extraction_status,
50700
50722
  embedding_model, extraction_model, created_at, updated_at, updated_by,
50701
50723
  source_type, source_id, source_path, runtime_path, idempotency_key, scope, agent_id, visibility,
50702
- memory_kind, evidence_meta)
50703
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(mem.id, mem.content, mem.normalizedContent ?? mem.content, mem.contentHash, mem.who, mem.why, mem.project, mem.importance, mem.type, mem.tags, mem.pinned, mem.isDeleted ?? 0, mem.extractionStatus ?? "none", mem.embeddingModel ?? null, mem.extractionModel ?? null, mem.createdAt, mem.createdAt, mem.updatedBy ?? mem.who, mem.sourceType, mem.sourceId, mem.sourcePath ?? null, mem.runtimePath ?? null, mem.idempotencyKey ?? null, mem.scope ?? null, mem.agentId ?? "default", mem.visibility ?? "global", mem.memoryKind ?? null, mem.evidenceMeta ?? null);
50724
+ memory_kind, evidence_meta, review_after)
50725
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(mem.id, mem.content, mem.normalizedContent ?? mem.content, mem.contentHash, mem.who, mem.why, mem.project, mem.importance, mem.type, mem.tags, mem.pinned, mem.isDeleted ?? 0, mem.extractionStatus ?? "none", mem.embeddingModel ?? null, mem.extractionModel ?? null, mem.createdAt, mem.createdAt, mem.updatedBy ?? mem.who, mem.sourceType, mem.sourceId, mem.sourcePath ?? null, mem.runtimePath ?? null, mem.idempotencyKey ?? null, mem.scope ?? null, mem.agentId ?? "default", mem.visibility ?? "global", mem.memoryKind ?? null, mem.evidenceMeta ?? null, mem.reviewAfter ?? null);
50704
50726
  return mem.id;
50705
50727
  }
50706
50728
  function txForgetMemory(db, input) {
@@ -50803,6 +50825,15 @@ function txSupersedeMemory(db, input) {
50803
50825
  currentSupersededBy: existing.superseded_by
50804
50826
  };
50805
50827
  }
50828
+ if (existing.superseded_by !== null && existing.superseded_by !== input.supersededBy) {
50829
+ return {
50830
+ status: "already_superseded_by_other",
50831
+ memoryId: input.memoryId,
50832
+ supersededBy: existing.superseded_by,
50833
+ currentVersion: existing.version,
50834
+ currentSupersededBy: existing.superseded_by
50835
+ };
50836
+ }
50806
50837
  const target2 = db.prepare(`SELECT id, is_deleted, agent_id, project, scope, visibility
50807
50838
  FROM memories
50808
50839
  WHERE id = ?`).get(input.supersededBy);
@@ -50875,7 +50906,7 @@ function applyOntologyOperationBatchInTx(db, params) {
50875
50906
  const row = getProposalInTx(db, inserted.id, params.agentId);
50876
50907
  if (row === null)
50877
50908
  throw new OntologyProposalError("Proposal not found", 404);
50878
- const result = applyOperation(db, row, params.actor);
50909
+ const result = applyOperation(db, row, params.actor, params.writeCaps);
50879
50910
  items.push({
50880
50911
  proposal: markAppliedInTx(db, row, params.actor, result),
50881
50912
  result,
@@ -50949,6 +50980,16 @@ function readString2(record5, key) {
50949
50980
  const trimmed = value.trim();
50950
50981
  return trimmed.length > 0 ? trimmed : null;
50951
50982
  }
50983
+ function readReviewAfter(payload) {
50984
+ const value = readString2(payload, "review_after");
50985
+ if (value === null)
50986
+ return null;
50987
+ const parsed = new Date(value);
50988
+ if (Number.isNaN(parsed.getTime())) {
50989
+ throw new OntologyProposalError("payload.review_after must be a valid ISO timestamp", 400);
50990
+ }
50991
+ return parsed.toISOString();
50992
+ }
50952
50993
  function readNumber(record5, key) {
50953
50994
  const value = record5[key];
50954
50995
  return typeof value === "number" && Number.isFinite(value) ? value : null;
@@ -51243,6 +51284,7 @@ function materializeAttributeMemoryInTx(db, input) {
51243
51284
  sourcePath: input.proposal.source_path,
51244
51285
  agentId: input.agentId,
51245
51286
  visibility: "global",
51287
+ reviewAfter: input.reviewAfter,
51246
51288
  createdAt: now()
51247
51289
  });
51248
51290
  }
@@ -51326,7 +51368,7 @@ function restoreAttributeMemoryInTx(db, memoryId, proposal) {
51326
51368
  createdAt: changedAt
51327
51369
  });
51328
51370
  }
51329
- function applyAddClaimValue(db, agentId, proposal, payload) {
51371
+ function applyAddClaimValue(db, agentId, proposal, payload, writeCaps) {
51330
51372
  const entity = readString2(payload, "entity");
51331
51373
  const aspect = readString2(payload, "aspect");
51332
51374
  const claimKey = readString2(payload, "claim_key");
@@ -51340,6 +51382,9 @@ function applyAddClaimValue(db, agentId, proposal, payload) {
51340
51382
  if (value === null)
51341
51383
  throw new OntologyProposalError("payload.value is required", 400);
51342
51384
  const entityId = resolveOrCreateEntity(db, agentId, entity, normalizeEntityType2(readString2(payload, "entity_type")));
51385
+ const addEntityRow = db.prepare("SELECT id, name FROM entities WHERE id = ?").get(entityId);
51386
+ if (addEntityRow !== undefined)
51387
+ enforceAspectCapForNewAspect(db, addEntityRow, agentId, aspect, writeCaps);
51343
51388
  const aspectId = resolveOrCreateAspect(db, entityId, agentId, aspect);
51344
51389
  const groupKey = readString2(payload, "group_key") ?? "general";
51345
51390
  const kind = normalizeAttributeKind(readString2(payload, "kind"));
@@ -51356,9 +51401,18 @@ function applyAddClaimValue(db, agentId, proposal, payload) {
51356
51401
  if (existing) {
51357
51402
  return { entityId, aspectId, attributeId: existing.id, deduped: true };
51358
51403
  }
51404
+ if (writeCaps !== undefined) {
51405
+ const attrCount = db.prepare(`SELECT COUNT(*) AS c FROM entity_attributes
51406
+ WHERE aspect_id = ? AND agent_id = ? AND status = 'active'`).get(aspectId, agentId);
51407
+ if (attrCount.c >= writeCaps.maxAttributesPerAspect) {
51408
+ const aspectName = db.prepare("SELECT name FROM entity_aspects WHERE id = ?").get(aspectId);
51409
+ throw new OntologyProposalError(`aspect '${aspectName?.name ?? aspectId}' is at attribute cap (${attrCount.c}/${writeCaps.maxAttributesPerAspect}) — supersede or expire an existing claim, or consolidate duplicates, before adding`, 409);
51410
+ }
51411
+ }
51359
51412
  const id = crypto.randomUUID();
51360
51413
  const confidence = clamp01(readNumber(payload, "confidence") ?? proposal.confidence);
51361
51414
  const importance = clamp01(readNumber(payload, "importance") ?? confidence);
51415
+ const reviewAfter = readReviewAfter(payload);
51362
51416
  const proposalEvidence = proposalAuditEvidence(proposal);
51363
51417
  db.prepare(`INSERT INTO entity_attributes
51364
51418
  (id, aspect_id, agent_id, kind, content, normalized_content,
@@ -51376,11 +51430,12 @@ function applyAddClaimValue(db, agentId, proposal, payload) {
51376
51430
  content: value,
51377
51431
  normalizedContent: normalized,
51378
51432
  importance,
51433
+ reviewAfter,
51379
51434
  proposal
51380
51435
  });
51381
51436
  return { entityId, aspectId, attributeId: id, memoryId, deduped: false };
51382
51437
  }
51383
- function applySetClaimValue(db, agentId, proposal, payload) {
51438
+ function applySetClaimValue(db, agentId, proposal, payload, writeCaps) {
51384
51439
  const entity = readString2(payload, "entity");
51385
51440
  const aspect = readString2(payload, "aspect");
51386
51441
  const claimKey = canonicalKey(readString2(payload, "claim_key") ?? readString2(payload, "claim"));
@@ -51422,11 +51477,20 @@ function applySetClaimValue(db, agentId, proposal, payload) {
51422
51477
  throw new OntologyProposalError("Refusing to replace active constraint claim without force", 409);
51423
51478
  }
51424
51479
  const previous = active[0] ?? slot[0] ?? null;
51480
+ if (previous === null && writeCaps !== undefined) {
51481
+ const attrCount = db.prepare(`SELECT COUNT(*) AS c FROM entity_attributes
51482
+ WHERE aspect_id = ? AND agent_id = ? AND status = 'active'`).get(aspectId, agentId);
51483
+ if (attrCount.c >= writeCaps.maxAttributesPerAspect) {
51484
+ const aspectName = db.prepare("SELECT name FROM entity_aspects WHERE id = ?").get(aspectId);
51485
+ throw new OntologyProposalError(`aspect '${aspectName?.name ?? aspectId}' is at attribute cap (${attrCount.c}/${writeCaps.maxAttributesPerAspect}) — supersede or expire an existing claim, or consolidate duplicates, before adding`, 409);
51486
+ }
51487
+ }
51425
51488
  const version2 = previous === null ? 1 : Math.max(...slot.map((row) => row.version ?? 1)) + 1;
51426
51489
  const rootId = previous?.version_root_id ?? previous?.id ?? crypto.randomUUID();
51427
51490
  const id = version2 === 1 ? rootId : crypto.randomUUID();
51428
51491
  const confidence = clamp01(readNumber(payload, "confidence") ?? proposal.confidence);
51429
51492
  const importance = clamp01(readNumber(payload, "importance") ?? confidence);
51493
+ const reviewAfter = readReviewAfter(payload);
51430
51494
  db.prepare(`INSERT INTO entity_attributes
51431
51495
  (id, aspect_id, agent_id, kind, content, normalized_content,
51432
51496
  confidence, importance, status, group_key, claim_key,
@@ -51442,6 +51506,7 @@ function applySetClaimValue(db, agentId, proposal, payload) {
51442
51506
  content: value,
51443
51507
  normalizedContent: normalized,
51444
51508
  importance,
51509
+ reviewAfter,
51445
51510
  proposal
51446
51511
  });
51447
51512
  if (active.length > 0) {
@@ -51591,7 +51656,21 @@ function applyArchiveEntity(db, agentId, proposal, payload, actor) {
51591
51656
  WHERE id = ? AND agent_id = ?`).run(actor, readString2(payload, "reason") ?? proposal.rationale, proposal.id, JSON.stringify(proposalAuditEvidence(proposal)), entity.id, agentId);
51592
51657
  return { entityId: entity.id, archived: true };
51593
51658
  }
51594
- function applyCreateAspect(db, agentId, proposal, payload) {
51659
+ function enforceAspectCapForNewAspect(db, entity, agentId, name, writeCaps) {
51660
+ if (writeCaps === undefined)
51661
+ return;
51662
+ const key = canonical(name);
51663
+ const activeAspect = db.prepare(`SELECT id FROM entity_aspects
51664
+ WHERE entity_id = ? AND agent_id = ? AND canonical_name = ? AND COALESCE(status, 'active') = 'active'`).get(entity.id, agentId, key);
51665
+ if (activeAspect != null)
51666
+ return;
51667
+ const aspectCount = db.prepare(`SELECT COUNT(*) AS c FROM entity_aspects
51668
+ WHERE entity_id = ? AND agent_id = ? AND COALESCE(status, 'active') = 'active'`).get(entity.id, agentId);
51669
+ if (aspectCount.c >= writeCaps.maxAspectsPerEntity) {
51670
+ throw new OntologyProposalError(`entity '${entity.name}' is at aspect cap (${aspectCount.c}/${writeCaps.maxAspectsPerEntity}) — consolidate or archive an existing aspect before creating a new one`, 409);
51671
+ }
51672
+ }
51673
+ function applyCreateAspect(db, agentId, proposal, payload, writeCaps) {
51595
51674
  const entitySelector = readPayloadSelector(payload, "entity", "entity_id");
51596
51675
  const name = readString2(payload, "name") ?? readString2(payload, "aspect");
51597
51676
  if (entitySelector === null)
@@ -51599,6 +51678,7 @@ function applyCreateAspect(db, agentId, proposal, payload) {
51599
51678
  if (name === null)
51600
51679
  throw new OntologyProposalError("payload.name is required", 400);
51601
51680
  const entity = resolveEntityStrict(db, agentId, entitySelector);
51681
+ enforceAspectCapForNewAspect(db, entity, agentId, name, writeCaps);
51602
51682
  const aspectId = resolveOrCreateAspect(db, entity.id, agentId, name);
51603
51683
  db.prepare(`UPDATE entity_aspects
51604
51684
  SET proposal_id = ?, proposal_evidence = ?, updated_at = datetime('now')
@@ -51762,6 +51842,68 @@ function applyMergeEntities(db, agentId, payload) {
51762
51842
  warnings: plan.warnings
51763
51843
  };
51764
51844
  }
51845
+ function applyMergeAspects(db, agentId, proposal, payload) {
51846
+ const entitySelector = readPayloadSelector(payload, "entity", "entity_id");
51847
+ const targetSelector = readPayloadSelector(payload, "target", "target_aspect", "target_aspect_id", "aspect");
51848
+ const rawSources = readStringArray(payload, "sources") ?? readStringArray(payload, "source_aspects");
51849
+ if (entitySelector === null)
51850
+ throw new OntologyProposalError("payload.entity is required", 400);
51851
+ if (targetSelector === null)
51852
+ throw new OntologyProposalError("payload.target is required", 400);
51853
+ if (rawSources.length === 0)
51854
+ throw new OntologyProposalError("payload.sources is required", 400);
51855
+ const entity = resolveEntityStrict(db, agentId, entitySelector);
51856
+ const target2 = resolveAspectStrict(db, agentId, entity.id, targetSelector);
51857
+ const newName = readString2(payload, "new_name");
51858
+ if (newName !== null) {
51859
+ const key = canonical(newName);
51860
+ const collision = db.prepare(`SELECT id FROM entity_aspects
51861
+ WHERE entity_id = ? AND agent_id = ? AND id != ?
51862
+ AND COALESCE(status, 'active') = 'active'
51863
+ AND (canonical_name = ? OR LOWER(name) = ?)
51864
+ LIMIT 1`).get(entity.id, agentId, target2.id, key, key);
51865
+ if (collision)
51866
+ throw new OntologyProposalError(`Aspect collides with "${newName}"`, 409);
51867
+ }
51868
+ const evidence = JSON.stringify(proposalAuditEvidence(proposal));
51869
+ const moved = [];
51870
+ let totalMoved = 0;
51871
+ const seen = new Set([target2.id]);
51872
+ for (const raw of rawSources) {
51873
+ if (typeof raw !== "string")
51874
+ throw new OntologyProposalError("payload.sources must be aspect selectors", 400);
51875
+ const source = resolveAspectStrict(db, agentId, entity.id, raw);
51876
+ if (seen.has(source.id))
51877
+ continue;
51878
+ seen.add(source.id);
51879
+ const attributes = db.prepare("SELECT id FROM entity_attributes WHERE aspect_id = ? AND agent_id = ?").all(source.id, agentId);
51880
+ for (const attribute of attributes) {
51881
+ db.prepare(`UPDATE entity_attributes
51882
+ SET aspect_id = ?, updated_at = datetime('now')
51883
+ WHERE id = ? AND agent_id = ?`).run(target2.id, attribute.id, agentId);
51884
+ }
51885
+ db.prepare(`UPDATE entity_aspects
51886
+ SET status = 'archived', archived_at = datetime('now'), archived_by = ?,
51887
+ archive_reason = ?, proposal_id = ?, proposal_evidence = ?, updated_at = datetime('now')
51888
+ WHERE id = ? AND agent_id = ? AND COALESCE(status, 'active') = 'active'`).run(proposal.created_by || "ontology-merge", `Merged into aspect '${target2.name}'`, proposal.id, evidence, source.id, agentId);
51889
+ moved.push({ aspect: source.name, attributes: attributes.length });
51890
+ totalMoved += attributes.length;
51891
+ }
51892
+ if (moved.length === 0)
51893
+ throw new OntologyProposalError("No distinct source aspects to merge", 400);
51894
+ if (newName !== null) {
51895
+ db.prepare(`UPDATE entity_aspects
51896
+ SET name = ?, canonical_name = ?, proposal_id = ?, proposal_evidence = ?, updated_at = datetime('now')
51897
+ WHERE id = ? AND agent_id = ?`).run(newName, canonical(newName), proposal.id, evidence, target2.id, agentId);
51898
+ }
51899
+ return {
51900
+ entityId: entity.id,
51901
+ targetAspectId: target2.id,
51902
+ targetAspect: newName ?? target2.name,
51903
+ mergedAspects: moved,
51904
+ totalAttributesMoved: totalMoved
51905
+ };
51906
+ }
51765
51907
  function applyCreateLink(db, agentId, proposal, payload) {
51766
51908
  const sourceIdSelector = readString2(payload, "source_entity_id");
51767
51909
  const targetIdSelector = readString2(payload, "target_entity_id");
@@ -51894,7 +52036,7 @@ function applyArchiveEntityAlias(db, agentId, proposal, payload) {
51894
52036
  throw new OntologyProposalError("Alias not found", 404);
51895
52037
  return { aliasId, entityId: entity.id, archived: true };
51896
52038
  }
51897
- function applyOperation(db, proposal, actor) {
52039
+ function applyOperation(db, proposal, actor, writeCaps) {
51898
52040
  const payload = parseJsonRecord(proposal.payload);
51899
52041
  if (proposal.operation === "create_entity")
51900
52042
  return applyCreateEntity(db, proposal.agent_id, proposal, payload);
@@ -51903,17 +52045,19 @@ function applyOperation(db, proposal, actor) {
51903
52045
  if (proposal.operation === "archive_entity")
51904
52046
  return applyArchiveEntity(db, proposal.agent_id, proposal, payload, actor);
51905
52047
  if (proposal.operation === "create_aspect")
51906
- return applyCreateAspect(db, proposal.agent_id, proposal, payload);
52048
+ return applyCreateAspect(db, proposal.agent_id, proposal, payload, writeCaps);
51907
52049
  if (proposal.operation === "rename_aspect")
51908
52050
  return applyRenameAspect(db, proposal.agent_id, proposal, payload);
51909
52051
  if (proposal.operation === "archive_aspect")
51910
52052
  return applyArchiveAspect(db, proposal.agent_id, proposal, payload, actor);
51911
52053
  if (proposal.operation === "add_claim_value")
51912
- return applyAddClaimValue(db, proposal.agent_id, proposal, payload);
52054
+ return applyAddClaimValue(db, proposal.agent_id, proposal, payload, writeCaps);
51913
52055
  if (proposal.operation === "set_claim_value")
51914
- return applySetClaimValue(db, proposal.agent_id, proposal, payload);
52056
+ return applySetClaimValue(db, proposal.agent_id, proposal, payload, writeCaps);
51915
52057
  if (proposal.operation === "merge_entities")
51916
52058
  return applyMergeEntities(db, proposal.agent_id, payload);
52059
+ if (proposal.operation === "merge_aspects")
52060
+ return applyMergeAspects(db, proposal.agent_id, proposal, payload);
51917
52061
  if (proposal.operation === "supersede_claim_value") {
51918
52062
  return applySupersedeClaimValue(db, proposal.agent_id, proposal, payload);
51919
52063
  }
@@ -52331,6 +52475,7 @@ var aspectName = text.describe("The specific domain of knowledge this claim belo
52331
52475
  var aspectId = text.describe("The stable id of an existing aspect.");
52332
52476
  var claimValue = text.describe("A complete atomic assertion that is understandable on its own.");
52333
52477
  var claimKey = text.describe("A stable semantic slot key for versions of the same claim.");
52478
+ var reviewAfter = text.describe("An ISO timestamp after which a future temporal claim must be reviewed. Omit for non-temporal claims.").optional();
52334
52479
  var linkType = exports_external.enum(DEPENDENCY_TYPES2).describe("The dependency type between the two entities.");
52335
52480
  function payload(shape) {
52336
52481
  return exports_external.object(shape).passthrough();
@@ -52351,9 +52496,16 @@ var DREAMING_ONTOLOGY_PAYLOAD_SCHEMAS = {
52351
52496
  survivor: entityId.describe("The entity that survives the merge."),
52352
52497
  ...reasonField
52353
52498
  }),
52499
+ merge_aspects: payload({
52500
+ entityId,
52501
+ target: aspectId.describe("The aspect that absorbs the sources. Use aspect_id."),
52502
+ sources: exports_external.array(aspectId).min(1).describe("Aspect ids to fold into the target. Attributes are moved, sources are archived."),
52503
+ newName: aspectName.describe("Optional new name for the merged aspect.").optional(),
52504
+ ...reasonField
52505
+ }),
52354
52506
  create_entity: payload({ name: entityName, type: entityType }),
52355
- add_claim_value: payload({ entityId, aspectId, claimKey, value: claimValue }),
52356
- set_claim_value: payload({ entityId, aspectId, claimKey, value: claimValue }),
52507
+ add_claim_value: payload({ entityId, aspectId, claimKey, value: claimValue, reviewAfter }),
52508
+ set_claim_value: payload({ entityId, aspectId, claimKey, value: claimValue, reviewAfter }),
52357
52509
  supersede_claim_value: payload({
52358
52510
  entityId,
52359
52511
  aspectId,
@@ -52406,6 +52558,7 @@ var DREAMING_ONTOLOGY_OPERATION_SCHEMA = exports_external.discriminatedUnion("op
52406
52558
  operation("update_link"),
52407
52559
  operation("archive_link"),
52408
52560
  operation("merge_entities"),
52561
+ operation("merge_aspects"),
52409
52562
  operation("supersede_claim_value"),
52410
52563
  operation("create_policy"),
52411
52564
  operation("create_action_type"),
@@ -52420,7 +52573,8 @@ var HYGIENE_ARCHIVE_OPS = new Set([
52420
52573
  "archive_aspect",
52421
52574
  "archive_claim_value",
52422
52575
  "archive_link",
52423
- "merge_entities"
52576
+ "merge_entities",
52577
+ "merge_aspects"
52424
52578
  ]);
52425
52579
  function citationRecord(value) {
52426
52580
  if (typeof value !== "object" || value === null || Array.isArray(value))
@@ -52535,6 +52689,10 @@ function attentionProvenance(accessor, agentId, operation2, mintedById) {
52535
52689
  const survivor = typeof payload2.survivor === "string" ? payload2.survivor : "";
52536
52690
  const groupIds = semanticDuplicateIds(accessor, agentId, attention.details.canonicalName ?? "");
52537
52691
  expectedTarget = attention.subjectRef === `duplicate:${attention.details.canonicalName}` && groupIds.size > 1 && groupIds.has(survivor) && targets.length >= 2 && targets.every((id) => groupIds.has(id)) && targets.includes(survivor) && targets.some((id) => id !== survivor);
52692
+ } else if (operation2.operation === "merge_aspects") {
52693
+ const sources = Array.isArray(payload2.sources) ? payload2.sources.filter((value) => typeof value === "string") : [];
52694
+ const flaggedAspect = attention.details.aspectId ?? attention.subjectRef.replace(/^aspect:/, "");
52695
+ expectedTarget = attention.subjectRef.startsWith("aspect:") && typeof payload2.target === "string" && sources.length >= 1 && sources.includes(flaggedAspect);
52538
52696
  }
52539
52697
  if (!expectedTarget)
52540
52698
  return null;
@@ -52641,6 +52799,15 @@ function toApplicatorPayload(accessor, agentId, operation2, payload2) {
52641
52799
  const sourceIds = targets.filter((id) => id !== survivor);
52642
52800
  return { target_entity_id: survivor, source_entity_ids: sourceIds };
52643
52801
  }
52802
+ case "merge_aspects": {
52803
+ const entityId2 = stringField(payload2, "entityId");
52804
+ const target3 = stringField(payload2, "target");
52805
+ const sources = stringArrayField(payload2, "sources");
52806
+ if (entityId2 === null || target3 === null || sources === null || sources.length === 0)
52807
+ return null;
52808
+ const name = lookupEntityName(accessor, agentId, entityId2);
52809
+ return name === null ? null : { entity: name, target: target3, sources, new_name: stringField(payload2, "newName") ?? undefined };
52810
+ }
52644
52811
  case "create_entity": {
52645
52812
  const name = stringField(payload2, "name");
52646
52813
  const type = stringField(payload2, "type");
@@ -52656,7 +52823,13 @@ function toApplicatorPayload(accessor, agentId, operation2, payload2) {
52656
52823
  return null;
52657
52824
  const name = lookupEntityName(accessor, agentId, entityId2);
52658
52825
  const aspect = lookupAspectName(accessor, agentId, entityId2, aspectId2);
52659
- return name === null || aspect === null ? null : { entity: name, aspect, claim_key: claimKey2, value };
52826
+ return name === null || aspect === null ? null : {
52827
+ entity: name,
52828
+ aspect,
52829
+ claim_key: claimKey2,
52830
+ value,
52831
+ ...stringField(payload2, "reviewAfter") ? { review_after: stringField(payload2, "reviewAfter") } : {}
52832
+ };
52660
52833
  }
52661
52834
  case "supersede_claim_value": {
52662
52835
  const entityId2 = stringField(payload2, "entityId");
@@ -52796,7 +52969,8 @@ function applyDreamingOperations(params) {
52796
52969
  const batch = applyOntologyOperationBatchInTx(db, {
52797
52970
  agentId: params.agentId,
52798
52971
  actor: params.actor,
52799
- operations: [entry.input]
52972
+ operations: [entry.input],
52973
+ writeCaps: params.writeCaps
52800
52974
  });
52801
52975
  db.exec(`RELEASE SAVEPOINT ${savepoint}`);
52802
52976
  if (entry.attentionId !== null) {
@@ -52937,6 +53111,77 @@ function readDreamingRunbook(accessor, agentId, limit = 5) {
52937
53111
  });
52938
53112
  }
52939
53113
 
53114
+ // ../../platform/daemon/src/pipeline/memory-review-due.ts
53115
+ function toReviewDueMemory(row) {
53116
+ return {
53117
+ id: row.id,
53118
+ content: row.content,
53119
+ type: row.type,
53120
+ importance: row.importance,
53121
+ reviewAfter: row.review_after,
53122
+ createdAt: row.created_at,
53123
+ agentId: row.agent_id,
53124
+ scope: row.scope,
53125
+ attributeId: row.attribute_id,
53126
+ entityId: row.entity_id,
53127
+ entityName: row.entity_name,
53128
+ aspectId: row.aspect_id,
53129
+ aspectName: row.aspect_name,
53130
+ claimKey: row.claim_key
53131
+ };
53132
+ }
53133
+ function reviewDueQuery(options) {
53134
+ return options.agentId ? {
53135
+ sql: "AND m.agent_id = ?",
53136
+ params: [options.agentId]
53137
+ } : { sql: "", params: [] };
53138
+ }
53139
+ var REVIEW_DUE_SELECT = `
53140
+ SELECT m.id, m.content, m.type, m.importance, m.review_after, m.created_at, m.agent_id, m.scope,
53141
+ ea.id AS attribute_id, a.entity_id, e.name AS entity_name,
53142
+ ea.aspect_id, a.name AS aspect_name, ea.claim_key
53143
+ FROM memories m
53144
+ LEFT JOIN entity_attributes ea
53145
+ ON ea.memory_id = m.id
53146
+ AND ea.agent_id = m.agent_id
53147
+ AND ea.status = 'active'
53148
+ LEFT JOIN entity_aspects a ON a.id = ea.aspect_id AND a.agent_id = ea.agent_id
53149
+ LEFT JOIN entities e ON e.id = a.entity_id AND e.agent_id = ea.agent_id
53150
+ WHERE m.review_after IS NOT NULL
53151
+ AND m.superseded_by IS NULL
53152
+ AND m.is_deleted = 0`;
53153
+ function findExpiredReviewDueMemories(accessor, now2 = new Date, opts = {}) {
53154
+ const limit = opts.limit ?? 50;
53155
+ const query = reviewDueQuery(opts);
53156
+ const rows = accessor.all(`${REVIEW_DUE_SELECT}
53157
+ AND m.review_after < ?
53158
+ ${query.sql}
53159
+ ORDER BY review_after ASC
53160
+ LIMIT ?`, now2.toISOString(), ...query.params, limit);
53161
+ return rows.map(toReviewDueMemory);
53162
+ }
53163
+ function findApproachingReviewDueMemories(accessor, now2 = new Date, opts = {}) {
53164
+ const windowMs = opts.expiringSoonMs ?? 7 * 24 * 60 * 60 * 1000;
53165
+ const limit = opts.limit ?? 50;
53166
+ const query = reviewDueQuery(opts);
53167
+ const horizon = new Date(now2.getTime() + windowMs);
53168
+ const rows = accessor.all(`${REVIEW_DUE_SELECT}
53169
+ AND m.review_after >= ?
53170
+ AND m.review_after <= ?
53171
+ ${query.sql}
53172
+ ORDER BY review_after ASC
53173
+ LIMIT ?`, now2.toISOString(), horizon.toISOString(), ...query.params, limit);
53174
+ return rows.map(toReviewDueMemory);
53175
+ }
53176
+ function collectReviewDueClaims(accessor, now2, options = {}) {
53177
+ const limit = options.limit ?? 50;
53178
+ const expired = findExpiredReviewDueMemories(accessor, now2, { ...options, limit });
53179
+ return {
53180
+ expired,
53181
+ approaching: expired.length >= limit ? [] : findApproachingReviewDueMemories(accessor, now2, { ...options, limit: limit - expired.length })
53182
+ };
53183
+ }
53184
+
52940
53185
  // ../../platform/daemon/src/pipeline/dreaming-capabilities.ts
52941
53186
  var bounded = (value, fallback, max) => Math.min(Math.max(Math.floor(value ?? fallback), 1), max);
52942
53187
  var MAX_EVIDENCE_EXCERPT_CHARS = 2000;
@@ -53251,23 +53496,55 @@ function createDreamingCapabilities(params) {
53251
53496
  }
53252
53497
  return { ok: true, passId: params.passId };
53253
53498
  }),
53254
- capability("attention_list", "List attention", "List attention records (the hygiene queue) by kind and resolution status. Omit agentId to see the whole install's queue (each record carries its owning agentId); pass agentId to narrow to one scope.", true, exports_external.object({
53499
+ capability("attention_list", "List attention", "List attention records by kind and resolution status. Use kind hygiene for the queue, or review_due for expired and approaching temporal claims. Omit agentId to see the whole install; pass agentId to narrow to one scope.", true, exports_external.object({
53255
53500
  agentId: exports_external.string().optional(),
53256
53501
  kind: exports_external.string().optional(),
53257
53502
  status: exports_external.enum(["pending", "resolved"]).optional(),
53258
53503
  limit: exports_external.number().finite().optional()
53259
- }), async ({ agentId: scopeId, kind, status, limit }) => ({
53260
- ok: true,
53261
- items: scopeId !== undefined ? getDreamingAttentionScoped(accessor, scopeId, {
53262
- kind,
53263
- status: status ?? "pending",
53264
- limit: bounded(limit, 20, 100)
53265
- }) : getDreamingAttentionAcrossScopes(accessor, {
53266
- kind,
53267
- status: status ?? "pending",
53268
- limit: bounded(limit, 50, 200)
53269
- })
53270
- })),
53504
+ }), async ({ agentId: scopeId, kind, status, limit }) => {
53505
+ if (kind === "review_due") {
53506
+ if (status === "resolved")
53507
+ return { ok: true, items: [] };
53508
+ const due = accessor.withReadDb((db) => collectReviewDueClaims({ all: (sql, ...params2) => db.prepare(sql).all(...params2) }, new Date, { agentId: scopeId, limit: bounded(limit, scopeId ? 50 : 100, scopeId ? 100 : 200) }));
53509
+ return {
53510
+ ok: true,
53511
+ items: [
53512
+ ...due.expired.map((item) => ({
53513
+ id: item.id,
53514
+ kind: "review_due",
53515
+ status: "pending",
53516
+ subjectRef: `memory:${item.id}`,
53517
+ details: { phase: "expired", ...item },
53518
+ priority: "high",
53519
+ createdAt: item.createdAt,
53520
+ agentId: item.agentId
53521
+ })),
53522
+ ...due.approaching.map((item) => ({
53523
+ id: item.id,
53524
+ kind: "review_due",
53525
+ status: "pending",
53526
+ subjectRef: `memory:${item.id}`,
53527
+ details: { phase: "approaching", ...item },
53528
+ priority: "normal",
53529
+ createdAt: item.createdAt,
53530
+ agentId: item.agentId
53531
+ }))
53532
+ ]
53533
+ };
53534
+ }
53535
+ return {
53536
+ ok: true,
53537
+ items: scopeId !== undefined ? getDreamingAttentionScoped(accessor, scopeId, {
53538
+ kind,
53539
+ status: status ?? "pending",
53540
+ limit: bounded(limit, 20, 100)
53541
+ }) : getDreamingAttentionAcrossScopes(accessor, {
53542
+ kind,
53543
+ status: status ?? "pending",
53544
+ limit: bounded(limit, 50, 200)
53545
+ })
53546
+ };
53547
+ }),
53271
53548
  capability("apply_ontology_ops", "Apply ontology operations", 'Apply every semantic write through the daemon audit seam in one batch, in one agent scope (pass the agentId whose graph you are maintaining — hygiene attention records belong to the agent that flagged them). Ops are processed in array order. Hygiene ops (flag, archive_*, merge_entities) cite provenance: "attention:$<index>" for a flag earlier in the same batch, or "attention:<uuid>" from a prior batch. Content-bearing ops cite evidence with exact quotes from canonical episodic evidence in that scope.', false, exports_external.object({
53272
53549
  agentId: exports_external.string().min(1),
53273
53550
  operations: exports_external.array(DREAMING_ONTOLOGY_OPERATION_SCHEMA).min(1).max(100)
@@ -53277,7 +53554,8 @@ function createDreamingCapabilities(params) {
53277
53554
  agentId: scopeId,
53278
53555
  actor,
53279
53556
  operations,
53280
- passId: params.passId
53557
+ passId: params.passId,
53558
+ writeCaps: params.writeCaps
53281
53559
  });
53282
53560
  params.onOperationsApplied?.(result, operations);
53283
53561
  return { ok: result.ok, ...result.error ? { error: result.error } : {}, items: result.items };
@@ -55319,6 +55597,8 @@ async function createMcpServer(opts) {
55319
55597
  sourceCreatedAt: exports_external.string().optional().describe("Source-system creation time for this memory"),
55320
55598
  validFrom: exports_external.string().optional().describe("Start of validity window for this memory"),
55321
55599
  validUntil: exports_external.string().optional().describe("End of validity window for this memory"),
55600
+ reviewAfter: exports_external.string().optional().describe("ISO timestamp when this temporal claim becomes due for review (issue #945)"),
55601
+ supersedes: exports_external.string().optional().describe("Id of an existing memory this write supersedes. The old row is marked superseded in the same transaction, wiring vN -> vN+1 lineage for drill-down history."),
55322
55602
  transcript: exports_external.string().optional().describe("Raw source text (conversation transcript) to preserve alongside extracted memory"),
55323
55603
  structured: exports_external.object({
55324
55604
  entities: exports_external.array(exports_external.object({
@@ -55370,7 +55650,9 @@ async function createMcpServer(opts) {
55370
55650
  observedAt,
55371
55651
  sourceCreatedAt,
55372
55652
  validFrom,
55373
- validUntil
55653
+ validUntil,
55654
+ reviewAfter: reviewAfter2,
55655
+ supersedes
55374
55656
  }) => {
55375
55657
  const result = await fetchDaemon(baseUrl, "/api/memory/remember", {
55376
55658
  method: "POST",
@@ -55387,7 +55669,9 @@ async function createMcpServer(opts) {
55387
55669
  observedAt,
55388
55670
  sourceCreatedAt,
55389
55671
  validFrom,
55390
- validUntil
55672
+ validUntil,
55673
+ reviewAfter: reviewAfter2,
55674
+ supersedes
55391
55675
  })
55392
55676
  });
55393
55677
  if (!result.ok) {
@@ -1,43 +1,43 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.164.5",
3
+ "version": "0.166.0",
4
4
  "assets": [
5
5
  {
6
6
  "name": "signet-darwin-arm64",
7
7
  "platform": "darwin-arm64",
8
- "sha256": "d386168c642a9911a93a51e3d3978c8ca26b032c79981b13f1f0edbf6a2d0f6f",
9
- "size": 116912800
8
+ "sha256": "155b6a8c4d9af1904f3593cb30e9325aeffc6274f45cac63ba31e6a31d1f5205",
9
+ "size": 116945824
10
10
  },
11
11
  {
12
12
  "name": "signet-darwin-x64",
13
13
  "platform": "darwin-x64",
14
- "sha256": "c17de1f83dd3ebd90fcc2605b1cf0a7e2ef314c89c50eac8ecabcf8eb41d1d41",
15
- "size": 121539136
14
+ "sha256": "106507b237601bf0e55c682756ac16c686c693e452f5642457f96b8464a59a4b",
15
+ "size": 121571904
16
16
  },
17
17
  {
18
18
  "name": "signet-linux-arm64",
19
19
  "platform": "linux-arm64",
20
- "sha256": "1be67fd25892c420bcf735f8c7645fd67ef359d4febec94bc8ba012147045d5a",
21
- "size": 154153310
20
+ "sha256": "ce016781de96917962c4388fd278f21125e8ec27faac8cd0bbe746c1577bf891",
21
+ "size": 154190762
22
22
  },
23
23
  {
24
24
  "name": "signet-linux-x64",
25
25
  "platform": "linux-x64",
26
- "sha256": "ea86f301f8438aae23bedf2dd098616622e8710733dc99a3c8a62a6303c493bc",
27
- "size": 154712295
26
+ "sha256": "259426f4e14fffd27d296ecbb40779d85909bc77b4feed11af2fd95f5799c2d7",
27
+ "size": 154749747
28
28
  },
29
29
  {
30
30
  "name": "signet-win32-x64.exe",
31
31
  "platform": "win32-x64",
32
- "sha256": "3deb03c683b1adb501823bdf26b219538b7d15864a5b3540bfc44efdf4ce7360",
33
- "size": 170845696
32
+ "sha256": "16f90b1a518e3f9a0631eb0bd722ed69f37a80d4a916e51cefc157aeb19215bf",
33
+ "size": 170883072
34
34
  }
35
35
  ],
36
36
  "components": {
37
37
  "connectors": {
38
- "url": "signet-connectors-0.164.5.tar.gz",
39
- "sha256": "dddb6b7dfc5464b255b692e9a2e17fd1b2eb34297e9bbe95f7ec39ede78f73c6",
40
- "size": 16162
38
+ "url": "signet-connectors-0.166.0.tar.gz",
39
+ "sha256": "e2050e9c1d4078a715b144762cfe50dfeb719311c07812ec489eeab7f9bc7ed9",
40
+ "size": 16093
41
41
  }
42
42
  }
43
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signetai",
3
- "version": "0.164.5",
3
+ "version": "0.166.0",
4
4
  "description": "Signet native CLI installer wrapper",
5
5
  "type": "module",
6
6
  "bin": {
@@ -65,10 +65,10 @@
65
65
  "access": "public"
66
66
  },
67
67
  "optionalDependencies": {
68
- "signetai-darwin-arm64": "https://github.com/Signet-AI/signetai/releases/download/v0.164.5/signetai-darwin-arm64-0.164.5.tgz",
69
- "signetai-darwin-x64": "https://github.com/Signet-AI/signetai/releases/download/v0.164.5/signetai-darwin-x64-0.164.5.tgz",
70
- "signetai-linux-arm64": "https://github.com/Signet-AI/signetai/releases/download/v0.164.5/signetai-linux-arm64-0.164.5.tgz",
71
- "signetai-linux-x64": "https://github.com/Signet-AI/signetai/releases/download/v0.164.5/signetai-linux-x64-0.164.5.tgz",
72
- "signetai-win32-x64": "https://github.com/Signet-AI/signetai/releases/download/v0.164.5/signetai-win32-x64-0.164.5.tgz"
68
+ "signetai-darwin-arm64": "https://github.com/Signet-AI/signetai/releases/download/v0.166.0/signetai-darwin-arm64-0.166.0.tgz",
69
+ "signetai-darwin-x64": "https://github.com/Signet-AI/signetai/releases/download/v0.166.0/signetai-darwin-x64-0.166.0.tgz",
70
+ "signetai-linux-arm64": "https://github.com/Signet-AI/signetai/releases/download/v0.166.0/signetai-linux-arm64-0.166.0.tgz",
71
+ "signetai-linux-x64": "https://github.com/Signet-AI/signetai/releases/download/v0.166.0/signetai-linux-x64-0.166.0.tgz",
72
+ "signetai-win32-x64": "https://github.com/Signet-AI/signetai/releases/download/v0.166.0/signetai-win32-x64-0.166.0.tgz"
73
73
  }
74
74
  }