@ghostpaw/affinity 0.1.4 → 0.1.6

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/index.cjs CHANGED
@@ -1454,8 +1454,8 @@ function searchContacts(db, query, filters, options) {
1454
1454
  GROUP BY c.id
1455
1455
  `;
1456
1456
  const rows = db.prepare(sql).all(
1457
- ...params,
1458
1457
  normalizedPattern,
1458
+ ...params,
1459
1459
  pattern,
1460
1460
  pattern,
1461
1461
  normalizedPattern
@@ -6443,12 +6443,43 @@ var manageAttributeTool = defineAffinityTool({
6443
6443
  });
6444
6444
 
6445
6445
  // src/tools/manage_commitment_tool.ts
6446
+ function normalizeParticipant(p) {
6447
+ let contactId = p.contactId;
6448
+ if (contactId === void 0 && p.contact != null) {
6449
+ const loc = p.contact;
6450
+ contactId = loc.contactId;
6451
+ }
6452
+ const result = {
6453
+ contactId,
6454
+ role: p.role
6455
+ };
6456
+ if (p.directionality !== void 0) {
6457
+ result.directionality = p.directionality;
6458
+ }
6459
+ return result;
6460
+ }
6461
+ function ensureOwnerParticipates(db, participants) {
6462
+ const ownerId = findOwnerContactId(db);
6463
+ if (ownerId === null) return participants;
6464
+ if (participants.some((p) => p.contactId === ownerId)) return participants;
6465
+ return [{ contactId: ownerId, role: "actor" }, ...participants];
6466
+ }
6467
+ function prepareParticipants(db, raw) {
6468
+ return ensureOwnerParticipates(db, raw.map(normalizeParticipant));
6469
+ }
6446
6470
  function manageCommitmentToolHandler(db, input) {
6447
6471
  return withToolHandling(() => {
6448
6472
  if (input.action === "record") {
6473
+ const patched = {
6474
+ ...input.input,
6475
+ participants: prepareParticipants(
6476
+ db,
6477
+ input.input.participants
6478
+ )
6479
+ };
6449
6480
  return mutationToolResult(
6450
6481
  "record",
6451
- recordCommitment(db, input.input),
6482
+ recordCommitment(db, patched),
6452
6483
  "commitment"
6453
6484
  );
6454
6485
  }
@@ -6466,7 +6497,7 @@ function manageCommitmentToolHandler(db, input) {
6466
6497
  }
6467
6498
  var manageCommitmentTool = defineAffinityTool({
6468
6499
  name: manageCommitmentToolName,
6469
- description: "Record or resolve one commitment such as a promise or agreement.",
6500
+ description: "Record or resolve one commitment such as a promise or agreement. The owner contact is auto-injected into participants if missing.",
6470
6501
  whenToUse: "Use this when the work is specifically about a commitment lifecycle rather than a general event.",
6471
6502
  whenNotToUse: "Do not use this for ordinary interactions or direct relationship state changes.",
6472
6503
  sideEffects: "writes_state",
@@ -6484,10 +6515,42 @@ var manageCommitmentTool = defineAffinityTool({
6484
6515
  inputSchema: objectSchema(
6485
6516
  {
6486
6517
  action: enumSchema("Operation to perform.", ["record", "resolve"]),
6487
- input: {
6488
- type: "object",
6489
- description: "Commitment payload. Required when action=record."
6490
- },
6518
+ input: objectSchema(
6519
+ {
6520
+ commitmentType: enumSchema("Promise or agreement.", [
6521
+ "promise",
6522
+ "agreement"
6523
+ ]),
6524
+ occurredAt: integerSchema(
6525
+ "When the commitment was made, Unix timestamp in milliseconds."
6526
+ ),
6527
+ summary: stringSchema("Commitment summary. Must be non-empty."),
6528
+ significance: integerSchema("Commitment significance from 1 to 10."),
6529
+ dueAt: integerSchema("Optional due timestamp."),
6530
+ participants: arraySchema(
6531
+ objectSchema(
6532
+ {
6533
+ contactId: integerSchema(
6534
+ "Participant contact id. The owner contact MUST be included."
6535
+ ),
6536
+ role: stringSchema("Participant role."),
6537
+ directionality: stringSchema("Optional directionality.")
6538
+ },
6539
+ ["contactId", "role"]
6540
+ ),
6541
+ "Participant list. Must be non-empty and include the owner contact."
6542
+ ),
6543
+ now: integerSchema("Optional timestamp override.")
6544
+ },
6545
+ [
6546
+ "commitmentType",
6547
+ "occurredAt",
6548
+ "summary",
6549
+ "significance",
6550
+ "participants"
6551
+ ],
6552
+ "Commitment payload. Required when action=record."
6553
+ ),
6491
6554
  commitmentEventId: integerSchema(
6492
6555
  "Commitment event id. Required when action=resolve."
6493
6556
  ),
@@ -6644,6 +6707,43 @@ var manageContactTool = defineAffinityTool({
6644
6707
  });
6645
6708
 
6646
6709
  // src/tools/manage_date_anchor_tool.ts
6710
+ function contactLocatorSchema4(description) {
6711
+ return {
6712
+ type: "object",
6713
+ properties: {
6714
+ contactId: integerSchema("Exact contact id."),
6715
+ identity: objectSchema(
6716
+ {
6717
+ type: stringSchema("Identity type."),
6718
+ value: stringSchema("Identity value.")
6719
+ },
6720
+ ["type", "value"],
6721
+ "Contact identity locator."
6722
+ )
6723
+ },
6724
+ description
6725
+ };
6726
+ }
6727
+ function linkLocatorSchema3(description) {
6728
+ return {
6729
+ type: "object",
6730
+ properties: {
6731
+ linkId: integerSchema("Exact link id."),
6732
+ endpoints: objectSchema(
6733
+ {
6734
+ fromContactId: integerSchema("From contact id."),
6735
+ toContactId: integerSchema("To contact id."),
6736
+ kind: stringSchema("Optional link kind."),
6737
+ role: stringSchema("Optional link role."),
6738
+ isStructural: booleanSchema("Optional structural discriminator.")
6739
+ },
6740
+ ["fromContactId", "toContactId"],
6741
+ "Endpoint-based link locator."
6742
+ )
6743
+ },
6744
+ description
6745
+ };
6746
+ }
6647
6747
  function resolveDateAnchorTarget(db, target) {
6648
6748
  if (target.kind === "contact") {
6649
6749
  const contact = resolveContactLocator(db, target.contact, "target.contact");
@@ -6709,17 +6809,65 @@ var manageDateAnchorTool = defineAffinityTool({
6709
6809
  inputSchema: objectSchema(
6710
6810
  {
6711
6811
  action: enumSchema("Operation to perform.", ["add", "revise", "remove"]),
6712
- input: {
6713
- type: "object",
6714
- description: "Date anchor payload. Required when action=add."
6715
- },
6812
+ input: objectSchema(
6813
+ {
6814
+ target: objectSchema(
6815
+ {
6816
+ kind: enumSchema("Target kind.", ["contact", "link"]),
6817
+ contact: contactLocatorSchema4(
6818
+ "Contact locator. Required when kind=contact. Provide contactId or identity."
6819
+ ),
6820
+ link: linkLocatorSchema3(
6821
+ "Link locator. Required when kind=link. Provide linkId or endpoints."
6822
+ )
6823
+ },
6824
+ ["kind"],
6825
+ "Date-anchor target."
6826
+ ),
6827
+ recurrenceKind: enumSchema("Recurrence kind.", [
6828
+ "birthday",
6829
+ "anniversary",
6830
+ "renewal",
6831
+ "memorial",
6832
+ "custom_yearly"
6833
+ ]),
6834
+ anchorMonth: integerSchema("Anchor month, 1\u201312."),
6835
+ anchorDay: integerSchema("Anchor day, 1\u201331 (validated per month)."),
6836
+ summary: stringSchema("Summary. Must be non-empty."),
6837
+ significance: integerSchema("Significance from 1 to 10."),
6838
+ now: integerSchema("Optional timestamp override."),
6839
+ force: booleanSchema("Whether to bypass duplicate checks.")
6840
+ },
6841
+ [
6842
+ "target",
6843
+ "recurrenceKind",
6844
+ "anchorMonth",
6845
+ "anchorDay",
6846
+ "summary",
6847
+ "significance"
6848
+ ],
6849
+ "Date anchor payload. Required when action=add."
6850
+ ),
6716
6851
  anchorEventId: integerSchema(
6717
6852
  "Anchor event id. Required when action=revise or remove."
6718
6853
  ),
6719
- patch: {
6720
- type: "object",
6721
- description: "Anchor patch. Required when action=revise."
6722
- },
6854
+ patch: objectSchema(
6855
+ {
6856
+ recurrenceKind: enumSchema("Optional recurrence kind.", [
6857
+ "birthday",
6858
+ "anniversary",
6859
+ "renewal",
6860
+ "memorial",
6861
+ "custom_yearly"
6862
+ ]),
6863
+ anchorMonth: integerSchema("Optional month, 1\u201312."),
6864
+ anchorDay: integerSchema("Optional day, 1\u201331."),
6865
+ summary: stringSchema("Optional summary."),
6866
+ significance: integerSchema("Optional significance, 1\u201310.")
6867
+ },
6868
+ [],
6869
+ "Anchor patch. Required when action=revise."
6870
+ ),
6723
6871
  options: objectSchema(
6724
6872
  {
6725
6873
  now: integerSchema("Optional timestamp."),
@@ -6921,7 +7069,7 @@ function verifyIdentity(db, identityId, verifiedAt) {
6921
7069
  }
6922
7070
 
6923
7071
  // src/tools/manage_identity_tool.ts
6924
- function contactLocatorSchema4(description) {
7072
+ function contactLocatorSchema5(description) {
6925
7073
  return {
6926
7074
  type: "object",
6927
7075
  properties: {
@@ -7000,7 +7148,7 @@ var manageIdentityTool = defineAffinityTool({
7000
7148
  "verify",
7001
7149
  "remove"
7002
7150
  ]),
7003
- contact: contactLocatorSchema4(
7151
+ contact: contactLocatorSchema5(
7004
7152
  "Target contact. Required when action=add. Provide contactId or identity."
7005
7153
  ),
7006
7154
  input: objectSchema(
@@ -7040,7 +7188,7 @@ var manageIdentityTool = defineAffinityTool({
7040
7188
  });
7041
7189
 
7042
7190
  // src/tools/manage_relationship_tool.ts
7043
- function contactLocatorSchema5(description) {
7191
+ function contactLocatorSchema6(description) {
7044
7192
  return {
7045
7193
  type: "object",
7046
7194
  properties: {
@@ -7057,7 +7205,7 @@ function contactLocatorSchema5(description) {
7057
7205
  description
7058
7206
  };
7059
7207
  }
7060
- function linkLocatorSchema3(description) {
7208
+ function linkLocatorSchema4(description) {
7061
7209
  return {
7062
7210
  type: "object",
7063
7211
  properties: {
@@ -7089,10 +7237,15 @@ function manageRelationshipToolHandler(db, input) {
7089
7237
  if (!to.ok) {
7090
7238
  return to.result;
7091
7239
  }
7240
+ const merged = {
7241
+ ...input.input,
7242
+ ...input.kind !== void 0 && input.input?.kind === void 0 ? { kind: input.kind } : {},
7243
+ ...input.role !== void 0 && input.input?.role === void 0 ? { role: input.role } : {}
7244
+ };
7092
7245
  return mutationToolResult(
7093
7246
  "seed_social_link",
7094
7247
  seedSocialLink(db, {
7095
- ...input.input,
7248
+ ...merged,
7096
7249
  fromContactId: from.value.id,
7097
7250
  toContactId: to.value.id
7098
7251
  }),
@@ -7130,10 +7283,15 @@ function manageRelationshipToolHandler(db, input) {
7130
7283
  if (!to.ok) {
7131
7284
  return to.result;
7132
7285
  }
7286
+ const merged = {
7287
+ ...input.input,
7288
+ ...input.kind !== void 0 && input.input?.kind === void 0 ? { kind: input.kind } : {},
7289
+ ...input.role !== void 0 && input.input?.role === void 0 ? { role: input.role } : {}
7290
+ };
7133
7291
  return mutationToolResult(
7134
7292
  "set_structural_tie",
7135
7293
  setStructuralTie(db, {
7136
- ...input.input,
7294
+ ...merged,
7137
7295
  fromContactId: from.value.id,
7138
7296
  toContactId: to.value.id
7139
7297
  }),
@@ -7167,7 +7325,9 @@ var manageRelationshipTool = defineAffinityTool({
7167
7325
  action: "Which relationship action to perform.",
7168
7326
  from: "Source contact locator for actions that create or set links.",
7169
7327
  to: "Destination contact locator for actions that create or set links.",
7170
- input: "Action-specific creation payload.",
7328
+ input: "Action-specific creation payload (alternative to top-level kind/role).",
7329
+ kind: "Link kind for seed_social_link or set_structural_tie. Can be passed here or inside input.",
7330
+ role: "Optional role for seed_social_link or set_structural_tie. Can be passed here or inside input.",
7171
7331
  link: "Target link locator for existing-link actions.",
7172
7332
  bond: "Replacement bond text or null.",
7173
7333
  state: "Replacement link state.",
@@ -7184,17 +7344,23 @@ var manageRelationshipTool = defineAffinityTool({
7184
7344
  "set_structural_tie",
7185
7345
  "remove_structural_tie"
7186
7346
  ]),
7187
- from: contactLocatorSchema5(
7347
+ from: contactLocatorSchema6(
7188
7348
  "Source contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity."
7189
7349
  ),
7190
- to: contactLocatorSchema5(
7350
+ to: contactLocatorSchema6(
7191
7351
  "Target contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity."
7192
7352
  ),
7353
+ kind: stringSchema(
7354
+ "Link kind. Required for seed_social_link and set_structural_tie. Can be passed here or inside input. For seed_social_link: personal, family, professional, romantic, care, service, observed, other_relational. For set_structural_tie: works_at, manages, member_of, married_to, partner_of, parent_of, child_of, sibling_of, friend_of, client_of, vendor_of, reports_to, belongs_to, other_structural."
7355
+ ),
7356
+ role: stringSchema(
7357
+ "Optional role for seed_social_link or set_structural_tie. Can be passed here or inside input."
7358
+ ),
7193
7359
  input: {
7194
7360
  type: "object",
7195
- description: "Payload for seed_social_link or set_structural_tie. Shape depends on action."
7361
+ description: "Full payload for seed_social_link or set_structural_tie. Alternative to top-level kind/role."
7196
7362
  },
7197
- link: linkLocatorSchema3(
7363
+ link: linkLocatorSchema4(
7198
7364
  "Target link. Required when action=revise_bond, override_state, or remove_structural_tie. Provide linkId or endpoints."
7199
7365
  ),
7200
7366
  bond: nullableStringSchema(
@@ -7223,7 +7389,7 @@ var manageRelationshipTool = defineAffinityTool({
7223
7389
  });
7224
7390
 
7225
7391
  // src/tools/merge_contacts_tool.ts
7226
- function contactLocatorSchema6(description) {
7392
+ function contactLocatorSchema7(description) {
7227
7393
  return {
7228
7394
  type: "object",
7229
7395
  properties: {
@@ -7280,10 +7446,10 @@ var mergeContactsTool = defineAffinityTool({
7280
7446
  outputDescription: "Returns the merge receipt with rewired entities and affected-link information.",
7281
7447
  inputSchema: objectSchema(
7282
7448
  {
7283
- winner: contactLocatorSchema6(
7449
+ winner: contactLocatorSchema7(
7284
7450
  "Winner contact. Provide contactId or identity."
7285
7451
  ),
7286
- loser: contactLocatorSchema6(
7452
+ loser: contactLocatorSchema7(
7287
7453
  "Loser contact. Provide contactId or identity."
7288
7454
  ),
7289
7455
  reasonSummary: stringSchema("Optional merge reason."),
@@ -7296,31 +7462,64 @@ var mergeContactsTool = defineAffinityTool({
7296
7462
  });
7297
7463
 
7298
7464
  // src/tools/record_event_tool.ts
7465
+ function normalizeParticipant2(p) {
7466
+ let contactId = p.contactId;
7467
+ if (contactId === void 0 && p.contact != null) {
7468
+ const loc = p.contact;
7469
+ contactId = loc.contactId;
7470
+ }
7471
+ const result = {
7472
+ contactId,
7473
+ role: p.role
7474
+ };
7475
+ if (p.directionality !== void 0) {
7476
+ result.directionality = p.directionality;
7477
+ }
7478
+ return result;
7479
+ }
7480
+ function ensureOwnerParticipates2(db, participants) {
7481
+ const ownerId = findOwnerContactId(db);
7482
+ if (ownerId === null) return participants;
7483
+ if (participants.some((p) => p.contactId === ownerId)) return participants;
7484
+ return [{ contactId: ownerId, role: "actor" }, ...participants];
7485
+ }
7486
+ function prepareParticipants2(db, raw, injectOwner) {
7487
+ const normalized = raw.map(normalizeParticipant2);
7488
+ return injectOwner ? ensureOwnerParticipates2(db, normalized) : normalized;
7489
+ }
7299
7490
  function recordEventToolHandler(db, input) {
7300
7491
  return withToolHandling(() => {
7492
+ const patched = {
7493
+ ...input.input,
7494
+ participants: prepareParticipants2(
7495
+ db,
7496
+ input.input.participants,
7497
+ input.kind !== "observation"
7498
+ )
7499
+ };
7301
7500
  switch (input.kind) {
7302
7501
  case "interaction":
7303
7502
  return mutationToolResult(
7304
7503
  "interaction",
7305
- recordInteraction(db, input.input),
7504
+ recordInteraction(db, patched),
7306
7505
  "event"
7307
7506
  );
7308
7507
  case "observation":
7309
7508
  return mutationToolResult(
7310
7509
  "observation",
7311
- recordObservation(db, input.input),
7510
+ recordObservation(db, patched),
7312
7511
  "event"
7313
7512
  );
7314
7513
  case "milestone":
7315
7514
  return mutationToolResult(
7316
7515
  "milestone",
7317
- recordMilestone(db, input.input),
7516
+ recordMilestone(db, patched),
7318
7517
  "event"
7319
7518
  );
7320
7519
  case "transaction":
7321
7520
  return mutationToolResult(
7322
7521
  "transaction",
7323
- recordTransaction(db, input.input),
7522
+ recordTransaction(db, patched),
7324
7523
  "event"
7325
7524
  );
7326
7525
  }
@@ -7328,7 +7527,7 @@ function recordEventToolHandler(db, input) {
7328
7527
  }
7329
7528
  var recordEventTool = defineAffinityTool({
7330
7529
  name: recordEventToolName,
7331
- description: "Record one social event such as an interaction, observation, milestone, or transaction.",
7530
+ description: "Record one social event such as an interaction, observation, milestone, or transaction. For interactions, milestones, and transactions the owner contact is auto-injected into participants if missing. Observations may omit the owner when recording third-party evidence.",
7332
7531
  whenToUse: "Use this when you are capturing something that happened in the relationship graph.",
7333
7532
  whenNotToUse: "Do not use this for commitments, date anchors, or direct relationship state overrides.",
7334
7533
  sideEffects: "writes_state",
@@ -7348,10 +7547,53 @@ var recordEventTool = defineAffinityTool({
7348
7547
  "milestone",
7349
7548
  "transaction"
7350
7549
  ]),
7351
- input: {
7352
- type: "object",
7353
- description: "Event payload. Shape depends on kind. Interaction events require a `type` subtype; others do not. Always required."
7354
- }
7550
+ input: objectSchema(
7551
+ {
7552
+ type: enumSchema(
7553
+ "Interaction subtype. Required when kind=interaction, omit otherwise.",
7554
+ [
7555
+ "conversation",
7556
+ "activity",
7557
+ "gift",
7558
+ "support",
7559
+ "conflict",
7560
+ "correction"
7561
+ ]
7562
+ ),
7563
+ occurredAt: integerSchema(
7564
+ "When the event occurred, Unix timestamp in milliseconds."
7565
+ ),
7566
+ summary: stringSchema("Event summary. Must be non-empty."),
7567
+ significance: integerSchema("Event significance from 1 to 10."),
7568
+ participants: arraySchema(
7569
+ objectSchema(
7570
+ {
7571
+ contactId: integerSchema(
7572
+ "Participant contact id. The owner contact MUST be included."
7573
+ ),
7574
+ role: enumSchema("Participant role.", [
7575
+ "actor",
7576
+ "recipient",
7577
+ "subject",
7578
+ "observer",
7579
+ "mentioned"
7580
+ ]),
7581
+ directionality: enumSchema("Optional directionality.", [
7582
+ "owner_initiated",
7583
+ "other_initiated",
7584
+ "mutual",
7585
+ "observed"
7586
+ ])
7587
+ },
7588
+ ["contactId", "role"]
7589
+ ),
7590
+ "Participant list. Must be non-empty and include the owner contact."
7591
+ ),
7592
+ now: integerSchema("Optional timestamp override.")
7593
+ },
7594
+ ["occurredAt", "summary", "significance", "participants"],
7595
+ "Event payload. Always required. Interaction events also require `type`."
7596
+ )
7355
7597
  },
7356
7598
  ["kind", "input"],
7357
7599
  "Record one social event."
@@ -7378,7 +7620,7 @@ function buildChartOptions(input) {
7378
7620
  ...input.contactIds === void 0 ? {} : { contactIds: input.contactIds }
7379
7621
  };
7380
7622
  }
7381
- function contactLocatorSchema7(description) {
7623
+ function contactLocatorSchema8(description) {
7382
7624
  return {
7383
7625
  type: "object",
7384
7626
  properties: {
@@ -7395,7 +7637,7 @@ function contactLocatorSchema7(description) {
7395
7637
  description
7396
7638
  };
7397
7639
  }
7398
- function linkLocatorSchema4(description) {
7640
+ function linkLocatorSchema5(description) {
7399
7641
  return {
7400
7642
  type: "object",
7401
7643
  properties: {
@@ -7814,8 +8056,8 @@ var reviewAffinityTool = defineAffinityTool({
7814
8056
  "graph.chart",
7815
8057
  "merges.history"
7816
8058
  ]),
7817
- contact: contactLocatorSchema7("Optional target contact locator."),
7818
- link: linkLocatorSchema4("Optional target link locator."),
8059
+ contact: contactLocatorSchema8("Optional target contact locator."),
8060
+ link: linkLocatorSchema5("Optional target link locator."),
7819
8061
  contactIds: arraySchema(
7820
8062
  integerSchema("Contact id."),
7821
8063
  "Optional contact-id subset."
package/dist/index.d.cts CHANGED
@@ -1362,7 +1362,9 @@ type ManageRelationshipToolInput = {
1362
1362
  action: "seed_social_link";
1363
1363
  from: ContactLocator;
1364
1364
  to: ContactLocator;
1365
- input: Omit<SeedSocialLinkInput, "fromContactId" | "toContactId">;
1365
+ input?: Omit<SeedSocialLinkInput, "fromContactId" | "toContactId">;
1366
+ kind?: string;
1367
+ role?: string;
1366
1368
  } | {
1367
1369
  action: "revise_bond";
1368
1370
  link: LinkLocator;
@@ -1377,7 +1379,9 @@ type ManageRelationshipToolInput = {
1377
1379
  action: "set_structural_tie";
1378
1380
  from: ContactLocator;
1379
1381
  to: ContactLocator;
1380
- input: Omit<SetStructuralTieInput, "fromContactId" | "toContactId">;
1382
+ input?: Omit<SetStructuralTieInput, "fromContactId" | "toContactId">;
1383
+ kind?: string;
1384
+ role?: string;
1381
1385
  } | {
1382
1386
  action: "remove_structural_tie";
1383
1387
  link: LinkLocator;
package/dist/index.d.ts CHANGED
@@ -1362,7 +1362,9 @@ type ManageRelationshipToolInput = {
1362
1362
  action: "seed_social_link";
1363
1363
  from: ContactLocator;
1364
1364
  to: ContactLocator;
1365
- input: Omit<SeedSocialLinkInput, "fromContactId" | "toContactId">;
1365
+ input?: Omit<SeedSocialLinkInput, "fromContactId" | "toContactId">;
1366
+ kind?: string;
1367
+ role?: string;
1366
1368
  } | {
1367
1369
  action: "revise_bond";
1368
1370
  link: LinkLocator;
@@ -1377,7 +1379,9 @@ type ManageRelationshipToolInput = {
1377
1379
  action: "set_structural_tie";
1378
1380
  from: ContactLocator;
1379
1381
  to: ContactLocator;
1380
- input: Omit<SetStructuralTieInput, "fromContactId" | "toContactId">;
1382
+ input?: Omit<SetStructuralTieInput, "fromContactId" | "toContactId">;
1383
+ kind?: string;
1384
+ role?: string;
1381
1385
  } | {
1382
1386
  action: "remove_structural_tie";
1383
1387
  link: LinkLocator;
package/dist/index.js CHANGED
@@ -1452,8 +1452,8 @@ function searchContacts(db, query, filters, options) {
1452
1452
  GROUP BY c.id
1453
1453
  `;
1454
1454
  const rows = db.prepare(sql).all(
1455
- ...params,
1456
1455
  normalizedPattern,
1456
+ ...params,
1457
1457
  pattern,
1458
1458
  pattern,
1459
1459
  normalizedPattern
@@ -6441,12 +6441,43 @@ var manageAttributeTool = defineAffinityTool({
6441
6441
  });
6442
6442
 
6443
6443
  // src/tools/manage_commitment_tool.ts
6444
+ function normalizeParticipant(p) {
6445
+ let contactId = p.contactId;
6446
+ if (contactId === void 0 && p.contact != null) {
6447
+ const loc = p.contact;
6448
+ contactId = loc.contactId;
6449
+ }
6450
+ const result = {
6451
+ contactId,
6452
+ role: p.role
6453
+ };
6454
+ if (p.directionality !== void 0) {
6455
+ result.directionality = p.directionality;
6456
+ }
6457
+ return result;
6458
+ }
6459
+ function ensureOwnerParticipates(db, participants) {
6460
+ const ownerId = findOwnerContactId(db);
6461
+ if (ownerId === null) return participants;
6462
+ if (participants.some((p) => p.contactId === ownerId)) return participants;
6463
+ return [{ contactId: ownerId, role: "actor" }, ...participants];
6464
+ }
6465
+ function prepareParticipants(db, raw) {
6466
+ return ensureOwnerParticipates(db, raw.map(normalizeParticipant));
6467
+ }
6444
6468
  function manageCommitmentToolHandler(db, input) {
6445
6469
  return withToolHandling(() => {
6446
6470
  if (input.action === "record") {
6471
+ const patched = {
6472
+ ...input.input,
6473
+ participants: prepareParticipants(
6474
+ db,
6475
+ input.input.participants
6476
+ )
6477
+ };
6447
6478
  return mutationToolResult(
6448
6479
  "record",
6449
- recordCommitment(db, input.input),
6480
+ recordCommitment(db, patched),
6450
6481
  "commitment"
6451
6482
  );
6452
6483
  }
@@ -6464,7 +6495,7 @@ function manageCommitmentToolHandler(db, input) {
6464
6495
  }
6465
6496
  var manageCommitmentTool = defineAffinityTool({
6466
6497
  name: manageCommitmentToolName,
6467
- description: "Record or resolve one commitment such as a promise or agreement.",
6498
+ description: "Record or resolve one commitment such as a promise or agreement. The owner contact is auto-injected into participants if missing.",
6468
6499
  whenToUse: "Use this when the work is specifically about a commitment lifecycle rather than a general event.",
6469
6500
  whenNotToUse: "Do not use this for ordinary interactions or direct relationship state changes.",
6470
6501
  sideEffects: "writes_state",
@@ -6482,10 +6513,42 @@ var manageCommitmentTool = defineAffinityTool({
6482
6513
  inputSchema: objectSchema(
6483
6514
  {
6484
6515
  action: enumSchema("Operation to perform.", ["record", "resolve"]),
6485
- input: {
6486
- type: "object",
6487
- description: "Commitment payload. Required when action=record."
6488
- },
6516
+ input: objectSchema(
6517
+ {
6518
+ commitmentType: enumSchema("Promise or agreement.", [
6519
+ "promise",
6520
+ "agreement"
6521
+ ]),
6522
+ occurredAt: integerSchema(
6523
+ "When the commitment was made, Unix timestamp in milliseconds."
6524
+ ),
6525
+ summary: stringSchema("Commitment summary. Must be non-empty."),
6526
+ significance: integerSchema("Commitment significance from 1 to 10."),
6527
+ dueAt: integerSchema("Optional due timestamp."),
6528
+ participants: arraySchema(
6529
+ objectSchema(
6530
+ {
6531
+ contactId: integerSchema(
6532
+ "Participant contact id. The owner contact MUST be included."
6533
+ ),
6534
+ role: stringSchema("Participant role."),
6535
+ directionality: stringSchema("Optional directionality.")
6536
+ },
6537
+ ["contactId", "role"]
6538
+ ),
6539
+ "Participant list. Must be non-empty and include the owner contact."
6540
+ ),
6541
+ now: integerSchema("Optional timestamp override.")
6542
+ },
6543
+ [
6544
+ "commitmentType",
6545
+ "occurredAt",
6546
+ "summary",
6547
+ "significance",
6548
+ "participants"
6549
+ ],
6550
+ "Commitment payload. Required when action=record."
6551
+ ),
6489
6552
  commitmentEventId: integerSchema(
6490
6553
  "Commitment event id. Required when action=resolve."
6491
6554
  ),
@@ -6642,6 +6705,43 @@ var manageContactTool = defineAffinityTool({
6642
6705
  });
6643
6706
 
6644
6707
  // src/tools/manage_date_anchor_tool.ts
6708
+ function contactLocatorSchema4(description) {
6709
+ return {
6710
+ type: "object",
6711
+ properties: {
6712
+ contactId: integerSchema("Exact contact id."),
6713
+ identity: objectSchema(
6714
+ {
6715
+ type: stringSchema("Identity type."),
6716
+ value: stringSchema("Identity value.")
6717
+ },
6718
+ ["type", "value"],
6719
+ "Contact identity locator."
6720
+ )
6721
+ },
6722
+ description
6723
+ };
6724
+ }
6725
+ function linkLocatorSchema3(description) {
6726
+ return {
6727
+ type: "object",
6728
+ properties: {
6729
+ linkId: integerSchema("Exact link id."),
6730
+ endpoints: objectSchema(
6731
+ {
6732
+ fromContactId: integerSchema("From contact id."),
6733
+ toContactId: integerSchema("To contact id."),
6734
+ kind: stringSchema("Optional link kind."),
6735
+ role: stringSchema("Optional link role."),
6736
+ isStructural: booleanSchema("Optional structural discriminator.")
6737
+ },
6738
+ ["fromContactId", "toContactId"],
6739
+ "Endpoint-based link locator."
6740
+ )
6741
+ },
6742
+ description
6743
+ };
6744
+ }
6645
6745
  function resolveDateAnchorTarget(db, target) {
6646
6746
  if (target.kind === "contact") {
6647
6747
  const contact = resolveContactLocator(db, target.contact, "target.contact");
@@ -6707,17 +6807,65 @@ var manageDateAnchorTool = defineAffinityTool({
6707
6807
  inputSchema: objectSchema(
6708
6808
  {
6709
6809
  action: enumSchema("Operation to perform.", ["add", "revise", "remove"]),
6710
- input: {
6711
- type: "object",
6712
- description: "Date anchor payload. Required when action=add."
6713
- },
6810
+ input: objectSchema(
6811
+ {
6812
+ target: objectSchema(
6813
+ {
6814
+ kind: enumSchema("Target kind.", ["contact", "link"]),
6815
+ contact: contactLocatorSchema4(
6816
+ "Contact locator. Required when kind=contact. Provide contactId or identity."
6817
+ ),
6818
+ link: linkLocatorSchema3(
6819
+ "Link locator. Required when kind=link. Provide linkId or endpoints."
6820
+ )
6821
+ },
6822
+ ["kind"],
6823
+ "Date-anchor target."
6824
+ ),
6825
+ recurrenceKind: enumSchema("Recurrence kind.", [
6826
+ "birthday",
6827
+ "anniversary",
6828
+ "renewal",
6829
+ "memorial",
6830
+ "custom_yearly"
6831
+ ]),
6832
+ anchorMonth: integerSchema("Anchor month, 1\u201312."),
6833
+ anchorDay: integerSchema("Anchor day, 1\u201331 (validated per month)."),
6834
+ summary: stringSchema("Summary. Must be non-empty."),
6835
+ significance: integerSchema("Significance from 1 to 10."),
6836
+ now: integerSchema("Optional timestamp override."),
6837
+ force: booleanSchema("Whether to bypass duplicate checks.")
6838
+ },
6839
+ [
6840
+ "target",
6841
+ "recurrenceKind",
6842
+ "anchorMonth",
6843
+ "anchorDay",
6844
+ "summary",
6845
+ "significance"
6846
+ ],
6847
+ "Date anchor payload. Required when action=add."
6848
+ ),
6714
6849
  anchorEventId: integerSchema(
6715
6850
  "Anchor event id. Required when action=revise or remove."
6716
6851
  ),
6717
- patch: {
6718
- type: "object",
6719
- description: "Anchor patch. Required when action=revise."
6720
- },
6852
+ patch: objectSchema(
6853
+ {
6854
+ recurrenceKind: enumSchema("Optional recurrence kind.", [
6855
+ "birthday",
6856
+ "anniversary",
6857
+ "renewal",
6858
+ "memorial",
6859
+ "custom_yearly"
6860
+ ]),
6861
+ anchorMonth: integerSchema("Optional month, 1\u201312."),
6862
+ anchorDay: integerSchema("Optional day, 1\u201331."),
6863
+ summary: stringSchema("Optional summary."),
6864
+ significance: integerSchema("Optional significance, 1\u201310.")
6865
+ },
6866
+ [],
6867
+ "Anchor patch. Required when action=revise."
6868
+ ),
6721
6869
  options: objectSchema(
6722
6870
  {
6723
6871
  now: integerSchema("Optional timestamp."),
@@ -6919,7 +7067,7 @@ function verifyIdentity(db, identityId, verifiedAt) {
6919
7067
  }
6920
7068
 
6921
7069
  // src/tools/manage_identity_tool.ts
6922
- function contactLocatorSchema4(description) {
7070
+ function contactLocatorSchema5(description) {
6923
7071
  return {
6924
7072
  type: "object",
6925
7073
  properties: {
@@ -6998,7 +7146,7 @@ var manageIdentityTool = defineAffinityTool({
6998
7146
  "verify",
6999
7147
  "remove"
7000
7148
  ]),
7001
- contact: contactLocatorSchema4(
7149
+ contact: contactLocatorSchema5(
7002
7150
  "Target contact. Required when action=add. Provide contactId or identity."
7003
7151
  ),
7004
7152
  input: objectSchema(
@@ -7038,7 +7186,7 @@ var manageIdentityTool = defineAffinityTool({
7038
7186
  });
7039
7187
 
7040
7188
  // src/tools/manage_relationship_tool.ts
7041
- function contactLocatorSchema5(description) {
7189
+ function contactLocatorSchema6(description) {
7042
7190
  return {
7043
7191
  type: "object",
7044
7192
  properties: {
@@ -7055,7 +7203,7 @@ function contactLocatorSchema5(description) {
7055
7203
  description
7056
7204
  };
7057
7205
  }
7058
- function linkLocatorSchema3(description) {
7206
+ function linkLocatorSchema4(description) {
7059
7207
  return {
7060
7208
  type: "object",
7061
7209
  properties: {
@@ -7087,10 +7235,15 @@ function manageRelationshipToolHandler(db, input) {
7087
7235
  if (!to.ok) {
7088
7236
  return to.result;
7089
7237
  }
7238
+ const merged = {
7239
+ ...input.input,
7240
+ ...input.kind !== void 0 && input.input?.kind === void 0 ? { kind: input.kind } : {},
7241
+ ...input.role !== void 0 && input.input?.role === void 0 ? { role: input.role } : {}
7242
+ };
7090
7243
  return mutationToolResult(
7091
7244
  "seed_social_link",
7092
7245
  seedSocialLink(db, {
7093
- ...input.input,
7246
+ ...merged,
7094
7247
  fromContactId: from.value.id,
7095
7248
  toContactId: to.value.id
7096
7249
  }),
@@ -7128,10 +7281,15 @@ function manageRelationshipToolHandler(db, input) {
7128
7281
  if (!to.ok) {
7129
7282
  return to.result;
7130
7283
  }
7284
+ const merged = {
7285
+ ...input.input,
7286
+ ...input.kind !== void 0 && input.input?.kind === void 0 ? { kind: input.kind } : {},
7287
+ ...input.role !== void 0 && input.input?.role === void 0 ? { role: input.role } : {}
7288
+ };
7131
7289
  return mutationToolResult(
7132
7290
  "set_structural_tie",
7133
7291
  setStructuralTie(db, {
7134
- ...input.input,
7292
+ ...merged,
7135
7293
  fromContactId: from.value.id,
7136
7294
  toContactId: to.value.id
7137
7295
  }),
@@ -7165,7 +7323,9 @@ var manageRelationshipTool = defineAffinityTool({
7165
7323
  action: "Which relationship action to perform.",
7166
7324
  from: "Source contact locator for actions that create or set links.",
7167
7325
  to: "Destination contact locator for actions that create or set links.",
7168
- input: "Action-specific creation payload.",
7326
+ input: "Action-specific creation payload (alternative to top-level kind/role).",
7327
+ kind: "Link kind for seed_social_link or set_structural_tie. Can be passed here or inside input.",
7328
+ role: "Optional role for seed_social_link or set_structural_tie. Can be passed here or inside input.",
7169
7329
  link: "Target link locator for existing-link actions.",
7170
7330
  bond: "Replacement bond text or null.",
7171
7331
  state: "Replacement link state.",
@@ -7182,17 +7342,23 @@ var manageRelationshipTool = defineAffinityTool({
7182
7342
  "set_structural_tie",
7183
7343
  "remove_structural_tie"
7184
7344
  ]),
7185
- from: contactLocatorSchema5(
7345
+ from: contactLocatorSchema6(
7186
7346
  "Source contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity."
7187
7347
  ),
7188
- to: contactLocatorSchema5(
7348
+ to: contactLocatorSchema6(
7189
7349
  "Target contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity."
7190
7350
  ),
7351
+ kind: stringSchema(
7352
+ "Link kind. Required for seed_social_link and set_structural_tie. Can be passed here or inside input. For seed_social_link: personal, family, professional, romantic, care, service, observed, other_relational. For set_structural_tie: works_at, manages, member_of, married_to, partner_of, parent_of, child_of, sibling_of, friend_of, client_of, vendor_of, reports_to, belongs_to, other_structural."
7353
+ ),
7354
+ role: stringSchema(
7355
+ "Optional role for seed_social_link or set_structural_tie. Can be passed here or inside input."
7356
+ ),
7191
7357
  input: {
7192
7358
  type: "object",
7193
- description: "Payload for seed_social_link or set_structural_tie. Shape depends on action."
7359
+ description: "Full payload for seed_social_link or set_structural_tie. Alternative to top-level kind/role."
7194
7360
  },
7195
- link: linkLocatorSchema3(
7361
+ link: linkLocatorSchema4(
7196
7362
  "Target link. Required when action=revise_bond, override_state, or remove_structural_tie. Provide linkId or endpoints."
7197
7363
  ),
7198
7364
  bond: nullableStringSchema(
@@ -7221,7 +7387,7 @@ var manageRelationshipTool = defineAffinityTool({
7221
7387
  });
7222
7388
 
7223
7389
  // src/tools/merge_contacts_tool.ts
7224
- function contactLocatorSchema6(description) {
7390
+ function contactLocatorSchema7(description) {
7225
7391
  return {
7226
7392
  type: "object",
7227
7393
  properties: {
@@ -7278,10 +7444,10 @@ var mergeContactsTool = defineAffinityTool({
7278
7444
  outputDescription: "Returns the merge receipt with rewired entities and affected-link information.",
7279
7445
  inputSchema: objectSchema(
7280
7446
  {
7281
- winner: contactLocatorSchema6(
7447
+ winner: contactLocatorSchema7(
7282
7448
  "Winner contact. Provide contactId or identity."
7283
7449
  ),
7284
- loser: contactLocatorSchema6(
7450
+ loser: contactLocatorSchema7(
7285
7451
  "Loser contact. Provide contactId or identity."
7286
7452
  ),
7287
7453
  reasonSummary: stringSchema("Optional merge reason."),
@@ -7294,31 +7460,64 @@ var mergeContactsTool = defineAffinityTool({
7294
7460
  });
7295
7461
 
7296
7462
  // src/tools/record_event_tool.ts
7463
+ function normalizeParticipant2(p) {
7464
+ let contactId = p.contactId;
7465
+ if (contactId === void 0 && p.contact != null) {
7466
+ const loc = p.contact;
7467
+ contactId = loc.contactId;
7468
+ }
7469
+ const result = {
7470
+ contactId,
7471
+ role: p.role
7472
+ };
7473
+ if (p.directionality !== void 0) {
7474
+ result.directionality = p.directionality;
7475
+ }
7476
+ return result;
7477
+ }
7478
+ function ensureOwnerParticipates2(db, participants) {
7479
+ const ownerId = findOwnerContactId(db);
7480
+ if (ownerId === null) return participants;
7481
+ if (participants.some((p) => p.contactId === ownerId)) return participants;
7482
+ return [{ contactId: ownerId, role: "actor" }, ...participants];
7483
+ }
7484
+ function prepareParticipants2(db, raw, injectOwner) {
7485
+ const normalized = raw.map(normalizeParticipant2);
7486
+ return injectOwner ? ensureOwnerParticipates2(db, normalized) : normalized;
7487
+ }
7297
7488
  function recordEventToolHandler(db, input) {
7298
7489
  return withToolHandling(() => {
7490
+ const patched = {
7491
+ ...input.input,
7492
+ participants: prepareParticipants2(
7493
+ db,
7494
+ input.input.participants,
7495
+ input.kind !== "observation"
7496
+ )
7497
+ };
7299
7498
  switch (input.kind) {
7300
7499
  case "interaction":
7301
7500
  return mutationToolResult(
7302
7501
  "interaction",
7303
- recordInteraction(db, input.input),
7502
+ recordInteraction(db, patched),
7304
7503
  "event"
7305
7504
  );
7306
7505
  case "observation":
7307
7506
  return mutationToolResult(
7308
7507
  "observation",
7309
- recordObservation(db, input.input),
7508
+ recordObservation(db, patched),
7310
7509
  "event"
7311
7510
  );
7312
7511
  case "milestone":
7313
7512
  return mutationToolResult(
7314
7513
  "milestone",
7315
- recordMilestone(db, input.input),
7514
+ recordMilestone(db, patched),
7316
7515
  "event"
7317
7516
  );
7318
7517
  case "transaction":
7319
7518
  return mutationToolResult(
7320
7519
  "transaction",
7321
- recordTransaction(db, input.input),
7520
+ recordTransaction(db, patched),
7322
7521
  "event"
7323
7522
  );
7324
7523
  }
@@ -7326,7 +7525,7 @@ function recordEventToolHandler(db, input) {
7326
7525
  }
7327
7526
  var recordEventTool = defineAffinityTool({
7328
7527
  name: recordEventToolName,
7329
- description: "Record one social event such as an interaction, observation, milestone, or transaction.",
7528
+ description: "Record one social event such as an interaction, observation, milestone, or transaction. For interactions, milestones, and transactions the owner contact is auto-injected into participants if missing. Observations may omit the owner when recording third-party evidence.",
7330
7529
  whenToUse: "Use this when you are capturing something that happened in the relationship graph.",
7331
7530
  whenNotToUse: "Do not use this for commitments, date anchors, or direct relationship state overrides.",
7332
7531
  sideEffects: "writes_state",
@@ -7346,10 +7545,53 @@ var recordEventTool = defineAffinityTool({
7346
7545
  "milestone",
7347
7546
  "transaction"
7348
7547
  ]),
7349
- input: {
7350
- type: "object",
7351
- description: "Event payload. Shape depends on kind. Interaction events require a `type` subtype; others do not. Always required."
7352
- }
7548
+ input: objectSchema(
7549
+ {
7550
+ type: enumSchema(
7551
+ "Interaction subtype. Required when kind=interaction, omit otherwise.",
7552
+ [
7553
+ "conversation",
7554
+ "activity",
7555
+ "gift",
7556
+ "support",
7557
+ "conflict",
7558
+ "correction"
7559
+ ]
7560
+ ),
7561
+ occurredAt: integerSchema(
7562
+ "When the event occurred, Unix timestamp in milliseconds."
7563
+ ),
7564
+ summary: stringSchema("Event summary. Must be non-empty."),
7565
+ significance: integerSchema("Event significance from 1 to 10."),
7566
+ participants: arraySchema(
7567
+ objectSchema(
7568
+ {
7569
+ contactId: integerSchema(
7570
+ "Participant contact id. The owner contact MUST be included."
7571
+ ),
7572
+ role: enumSchema("Participant role.", [
7573
+ "actor",
7574
+ "recipient",
7575
+ "subject",
7576
+ "observer",
7577
+ "mentioned"
7578
+ ]),
7579
+ directionality: enumSchema("Optional directionality.", [
7580
+ "owner_initiated",
7581
+ "other_initiated",
7582
+ "mutual",
7583
+ "observed"
7584
+ ])
7585
+ },
7586
+ ["contactId", "role"]
7587
+ ),
7588
+ "Participant list. Must be non-empty and include the owner contact."
7589
+ ),
7590
+ now: integerSchema("Optional timestamp override.")
7591
+ },
7592
+ ["occurredAt", "summary", "significance", "participants"],
7593
+ "Event payload. Always required. Interaction events also require `type`."
7594
+ )
7353
7595
  },
7354
7596
  ["kind", "input"],
7355
7597
  "Record one social event."
@@ -7376,7 +7618,7 @@ function buildChartOptions(input) {
7376
7618
  ...input.contactIds === void 0 ? {} : { contactIds: input.contactIds }
7377
7619
  };
7378
7620
  }
7379
- function contactLocatorSchema7(description) {
7621
+ function contactLocatorSchema8(description) {
7380
7622
  return {
7381
7623
  type: "object",
7382
7624
  properties: {
@@ -7393,7 +7635,7 @@ function contactLocatorSchema7(description) {
7393
7635
  description
7394
7636
  };
7395
7637
  }
7396
- function linkLocatorSchema4(description) {
7638
+ function linkLocatorSchema5(description) {
7397
7639
  return {
7398
7640
  type: "object",
7399
7641
  properties: {
@@ -7812,8 +8054,8 @@ var reviewAffinityTool = defineAffinityTool({
7812
8054
  "graph.chart",
7813
8055
  "merges.history"
7814
8056
  ]),
7815
- contact: contactLocatorSchema7("Optional target contact locator."),
7816
- link: linkLocatorSchema4("Optional target link locator."),
8057
+ contact: contactLocatorSchema8("Optional target contact locator."),
8058
+ link: linkLocatorSchema5("Optional target link locator."),
7817
8059
  contactIds: arraySchema(
7818
8060
  integerSchema("Contact id."),
7819
8061
  "Optional contact-id subset."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostpaw/affinity",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Relationship intelligence and social CRM model for Node.js — SQLite, zero dependencies, with a built-in LLM tool facade",
5
5
  "license": "MIT",
6
6
  "author": {