@ghostpaw/affinity 0.1.5 → 0.2.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/index.cjs +554 -41
- package/dist/index.d.cts +58 -8
- package/dist/index.d.ts +58 -8
- package/dist/index.js +554 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -388,11 +388,32 @@ function initContactMergesTables(db) {
|
|
|
388
388
|
);
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
// src/merges/init_dismissed_duplicates_tables.ts
|
|
392
|
+
function initDismissedDuplicatesTables(db) {
|
|
393
|
+
db.exec(`
|
|
394
|
+
CREATE TABLE IF NOT EXISTS dismissed_duplicates (
|
|
395
|
+
left_id INTEGER NOT NULL,
|
|
396
|
+
right_id INTEGER NOT NULL,
|
|
397
|
+
reason TEXT,
|
|
398
|
+
dismissed_at INTEGER NOT NULL,
|
|
399
|
+
PRIMARY KEY (left_id, right_id),
|
|
400
|
+
CHECK (left_id < right_id)
|
|
401
|
+
)
|
|
402
|
+
`);
|
|
403
|
+
db.exec(
|
|
404
|
+
"CREATE INDEX IF NOT EXISTS idx_dismissed_duplicates_left ON dismissed_duplicates(left_id)"
|
|
405
|
+
);
|
|
406
|
+
db.exec(
|
|
407
|
+
"CREATE INDEX IF NOT EXISTS idx_dismissed_duplicates_right ON dismissed_duplicates(right_id)"
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
391
411
|
// src/init_affinity_tables.ts
|
|
392
412
|
function initAffinityTables(db) {
|
|
393
413
|
initContactsTables(db);
|
|
394
414
|
initContactRollupsTables(db);
|
|
395
415
|
initContactMergesTables(db);
|
|
416
|
+
initDismissedDuplicatesTables(db);
|
|
396
417
|
initIdentitiesTables(db);
|
|
397
418
|
initLinksTables(db);
|
|
398
419
|
initLinkRollupsTables(db);
|
|
@@ -500,6 +521,7 @@ __export(read_exports, {
|
|
|
500
521
|
getMergeHistory: () => getMergeHistory,
|
|
501
522
|
getOwnerProfile: () => getOwnerProfile,
|
|
502
523
|
listContacts: () => listContacts,
|
|
524
|
+
listDismissedDuplicates: () => listDismissedDuplicates,
|
|
503
525
|
listDuplicateCandidates: () => listDuplicateCandidates,
|
|
504
526
|
listMoments: () => listMoments,
|
|
505
527
|
listObservedLinks: () => listObservedLinks,
|
|
@@ -1352,8 +1374,24 @@ function listDuplicateCandidates(db, filters, options) {
|
|
|
1352
1374
|
}
|
|
1353
1375
|
}
|
|
1354
1376
|
}
|
|
1377
|
+
const dismissedRows = db.prepare("SELECT left_id, right_id FROM dismissed_duplicates").all();
|
|
1378
|
+
const dismissedSet = new Set(
|
|
1379
|
+
dismissedRows.map((r) => pairKey(r.left_id, r.right_id))
|
|
1380
|
+
);
|
|
1381
|
+
const includeDismissed = filters?.includeDismissed === true;
|
|
1382
|
+
for (const candidate of pairs.values()) {
|
|
1383
|
+
if (dismissedSet.has(
|
|
1384
|
+
pairKey(candidate.leftContactId, candidate.rightContactId)
|
|
1385
|
+
)) {
|
|
1386
|
+
candidate.dismissed = true;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1355
1389
|
const minScore = filters?.minScore ?? 0;
|
|
1356
|
-
return [...pairs.values()].filter((candidate) =>
|
|
1390
|
+
return [...pairs.values()].filter((candidate) => {
|
|
1391
|
+
if (candidate.matchScore < minScore) return false;
|
|
1392
|
+
if (candidate.dismissed === true && !includeDismissed) return false;
|
|
1393
|
+
return true;
|
|
1394
|
+
}).sort((left, right) => {
|
|
1357
1395
|
const leftExact = left.matchScore === 1 ? 1 : 0;
|
|
1358
1396
|
const rightExact = right.matchScore === 1 ? 1 : 0;
|
|
1359
1397
|
return rightExact - leftExact || right.matchScore - left.matchScore;
|
|
@@ -3416,6 +3454,12 @@ function mergeContacts(db, input) {
|
|
|
3416
3454
|
`INSERT INTO contact_merges (winner_contact_id, loser_contact_id, merged_at, reason_summary, manual)
|
|
3417
3455
|
VALUES (?, ?, ?, ?, 1)`
|
|
3418
3456
|
).run(winnerId, loserId, now, reasonSummary);
|
|
3457
|
+
db.prepare(
|
|
3458
|
+
"DELETE FROM dismissed_duplicates WHERE left_id = ? OR right_id = ?"
|
|
3459
|
+
).run(loserId, loserId);
|
|
3460
|
+
db.prepare(
|
|
3461
|
+
"DELETE FROM dismissed_duplicates WHERE left_id = ? OR right_id = ?"
|
|
3462
|
+
).run(winnerId, winnerId);
|
|
3419
3463
|
db.prepare(
|
|
3420
3464
|
"DELETE FROM link_rollups WHERE link_id IN (SELECT id FROM links WHERE removed_at IS NOT NULL)"
|
|
3421
3465
|
).run();
|
|
@@ -3443,6 +3487,117 @@ function mergeContacts(db, input) {
|
|
|
3443
3487
|
});
|
|
3444
3488
|
}
|
|
3445
3489
|
|
|
3490
|
+
// src/merges/receipts.ts
|
|
3491
|
+
function buildDismissalMutationReceipt(primary, partial) {
|
|
3492
|
+
const normalized = normalizeMutationReceiptPartial(partial);
|
|
3493
|
+
return {
|
|
3494
|
+
primary,
|
|
3495
|
+
created: normalized.created,
|
|
3496
|
+
updated: normalized.updated,
|
|
3497
|
+
archived: normalized.archived,
|
|
3498
|
+
removed: normalized.removed,
|
|
3499
|
+
affectedLinks: normalized.affectedLinks,
|
|
3500
|
+
derivedEffects: normalized.derivedEffects
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
// src/merges/dismiss_duplicate.ts
|
|
3505
|
+
function canonicalize(a, b) {
|
|
3506
|
+
return {
|
|
3507
|
+
leftContactId: Math.min(a, b),
|
|
3508
|
+
rightContactId: Math.max(a, b)
|
|
3509
|
+
};
|
|
3510
|
+
}
|
|
3511
|
+
function dismissDuplicate(db, leftContactId, rightContactId, reason, now) {
|
|
3512
|
+
return withTransaction(db, () => {
|
|
3513
|
+
if (leftContactId === rightContactId) {
|
|
3514
|
+
throw new AffinityValidationError(
|
|
3515
|
+
"cannot dismiss a contact as a duplicate of itself"
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
const left = getContactRowById(db, leftContactId);
|
|
3519
|
+
if (!left) {
|
|
3520
|
+
throw new AffinityNotFoundError(`contact ${leftContactId} not found`);
|
|
3521
|
+
}
|
|
3522
|
+
if (left.lifecycle_state === "merged") {
|
|
3523
|
+
throw new AffinityStateError(
|
|
3524
|
+
`contact ${leftContactId} is merged and cannot be dismissed`
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
const right = getContactRowById(db, rightContactId);
|
|
3528
|
+
if (!right) {
|
|
3529
|
+
throw new AffinityNotFoundError(`contact ${rightContactId} not found`);
|
|
3530
|
+
}
|
|
3531
|
+
if (right.lifecycle_state === "merged") {
|
|
3532
|
+
throw new AffinityStateError(
|
|
3533
|
+
`contact ${rightContactId} is merged and cannot be dismissed`
|
|
3534
|
+
);
|
|
3535
|
+
}
|
|
3536
|
+
const pair = canonicalize(leftContactId, rightContactId);
|
|
3537
|
+
const resolvedNow = resolveNow(now);
|
|
3538
|
+
const resolvedReason = reason === void 0 || reason === null ? null : (() => {
|
|
3539
|
+
const t = reason.trim();
|
|
3540
|
+
return t.length === 0 ? null : t;
|
|
3541
|
+
})();
|
|
3542
|
+
db.prepare(
|
|
3543
|
+
`INSERT INTO dismissed_duplicates (left_id, right_id, reason, dismissed_at)
|
|
3544
|
+
VALUES (?, ?, ?, ?)
|
|
3545
|
+
ON CONFLICT(left_id, right_id) DO UPDATE SET
|
|
3546
|
+
reason = excluded.reason,
|
|
3547
|
+
dismissed_at = excluded.dismissed_at`
|
|
3548
|
+
).run(pair.leftContactId, pair.rightContactId, resolvedReason, resolvedNow);
|
|
3549
|
+
return buildDismissalMutationReceipt(pair);
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
// src/merges/undismiss_duplicate.ts
|
|
3554
|
+
function canonicalize2(a, b) {
|
|
3555
|
+
return {
|
|
3556
|
+
leftContactId: Math.min(a, b),
|
|
3557
|
+
rightContactId: Math.max(a, b)
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3560
|
+
function undismissDuplicate(db, leftContactId, rightContactId) {
|
|
3561
|
+
return withTransaction(db, () => {
|
|
3562
|
+
if (leftContactId === rightContactId) {
|
|
3563
|
+
throw new AffinityValidationError(
|
|
3564
|
+
"cannot undismiss a contact from itself"
|
|
3565
|
+
);
|
|
3566
|
+
}
|
|
3567
|
+
const left = getContactRowById(db, leftContactId);
|
|
3568
|
+
if (!left) {
|
|
3569
|
+
throw new AffinityNotFoundError(`contact ${leftContactId} not found`);
|
|
3570
|
+
}
|
|
3571
|
+
const right = getContactRowById(db, rightContactId);
|
|
3572
|
+
if (!right) {
|
|
3573
|
+
throw new AffinityNotFoundError(`contact ${rightContactId} not found`);
|
|
3574
|
+
}
|
|
3575
|
+
const pair = canonicalize2(leftContactId, rightContactId);
|
|
3576
|
+
db.prepare(
|
|
3577
|
+
"DELETE FROM dismissed_duplicates WHERE left_id = ? AND right_id = ?"
|
|
3578
|
+
).run(pair.leftContactId, pair.rightContactId);
|
|
3579
|
+
return buildDismissalMutationReceipt(pair);
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
// src/merges/list_dismissed_duplicates.ts
|
|
3584
|
+
function listDismissedDuplicates(db, options) {
|
|
3585
|
+
assertDefaultOrdering("listDismissedDuplicates", options);
|
|
3586
|
+
const { limit, offset } = resolveListLimitOffset(options);
|
|
3587
|
+
const rows = db.prepare(
|
|
3588
|
+
`SELECT left_id, right_id, reason, dismissed_at
|
|
3589
|
+
FROM dismissed_duplicates
|
|
3590
|
+
ORDER BY dismissed_at DESC
|
|
3591
|
+
LIMIT ? OFFSET ?`
|
|
3592
|
+
).all(limit, offset);
|
|
3593
|
+
return rows.map((row) => ({
|
|
3594
|
+
leftContactId: row.left_id,
|
|
3595
|
+
rightContactId: row.right_id,
|
|
3596
|
+
reason: row.reason,
|
|
3597
|
+
dismissedAt: row.dismissed_at
|
|
3598
|
+
}));
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3446
3601
|
// src/events/get_contact_journal.ts
|
|
3447
3602
|
function getContactJournal(db, contactId, options) {
|
|
3448
3603
|
assertDefaultOrdering("getContactJournal", options);
|
|
@@ -5331,6 +5486,9 @@ __export(tools_exports, {
|
|
|
5331
5486
|
manageDateAnchorTool: () => manageDateAnchorTool,
|
|
5332
5487
|
manageDateAnchorToolHandler: () => manageDateAnchorToolHandler,
|
|
5333
5488
|
manageDateAnchorToolName: () => manageDateAnchorToolName,
|
|
5489
|
+
manageDuplicateDismissalTool: () => manageDuplicateDismissalTool,
|
|
5490
|
+
manageDuplicateDismissalToolHandler: () => manageDuplicateDismissalToolHandler,
|
|
5491
|
+
manageDuplicateDismissalToolName: () => manageDuplicateDismissalToolName,
|
|
5334
5492
|
manageIdentityTool: () => manageIdentityTool,
|
|
5335
5493
|
manageIdentityToolHandler: () => manageIdentityToolHandler,
|
|
5336
5494
|
manageIdentityToolName: () => manageIdentityToolName,
|
|
@@ -5453,6 +5611,7 @@ var reviewAffinityToolName = "review_affinity";
|
|
|
5453
5611
|
var inspectAffinityItemToolName = "inspect_affinity_item";
|
|
5454
5612
|
var manageContactToolName = "manage_contact";
|
|
5455
5613
|
var mergeContactsToolName = "merge_contacts";
|
|
5614
|
+
var manageDuplicateDismissalToolName = "manage_duplicate_dismissal";
|
|
5456
5615
|
var manageIdentityToolName = "manage_identity";
|
|
5457
5616
|
var manageRelationshipToolName = "manage_relationship";
|
|
5458
5617
|
var recordEventToolName = "record_event";
|
|
@@ -6441,12 +6600,43 @@ var manageAttributeTool = defineAffinityTool({
|
|
|
6441
6600
|
});
|
|
6442
6601
|
|
|
6443
6602
|
// src/tools/manage_commitment_tool.ts
|
|
6603
|
+
function normalizeParticipant(p) {
|
|
6604
|
+
let contactId = p.contactId;
|
|
6605
|
+
if (contactId === void 0 && p.contact != null) {
|
|
6606
|
+
const loc = p.contact;
|
|
6607
|
+
contactId = loc.contactId;
|
|
6608
|
+
}
|
|
6609
|
+
const result = {
|
|
6610
|
+
contactId,
|
|
6611
|
+
role: p.role
|
|
6612
|
+
};
|
|
6613
|
+
if (p.directionality !== void 0) {
|
|
6614
|
+
result.directionality = p.directionality;
|
|
6615
|
+
}
|
|
6616
|
+
return result;
|
|
6617
|
+
}
|
|
6618
|
+
function ensureOwnerParticipates(db, participants) {
|
|
6619
|
+
const ownerId = findOwnerContactId(db);
|
|
6620
|
+
if (ownerId === null) return participants;
|
|
6621
|
+
if (participants.some((p) => p.contactId === ownerId)) return participants;
|
|
6622
|
+
return [{ contactId: ownerId, role: "actor" }, ...participants];
|
|
6623
|
+
}
|
|
6624
|
+
function prepareParticipants(db, raw) {
|
|
6625
|
+
return ensureOwnerParticipates(db, raw.map(normalizeParticipant));
|
|
6626
|
+
}
|
|
6444
6627
|
function manageCommitmentToolHandler(db, input) {
|
|
6445
6628
|
return withToolHandling(() => {
|
|
6446
6629
|
if (input.action === "record") {
|
|
6630
|
+
const patched = {
|
|
6631
|
+
...input.input,
|
|
6632
|
+
participants: prepareParticipants(
|
|
6633
|
+
db,
|
|
6634
|
+
input.input.participants
|
|
6635
|
+
)
|
|
6636
|
+
};
|
|
6447
6637
|
return mutationToolResult(
|
|
6448
6638
|
"record",
|
|
6449
|
-
recordCommitment(db,
|
|
6639
|
+
recordCommitment(db, patched),
|
|
6450
6640
|
"commitment"
|
|
6451
6641
|
);
|
|
6452
6642
|
}
|
|
@@ -6464,7 +6654,7 @@ function manageCommitmentToolHandler(db, input) {
|
|
|
6464
6654
|
}
|
|
6465
6655
|
var manageCommitmentTool = defineAffinityTool({
|
|
6466
6656
|
name: manageCommitmentToolName,
|
|
6467
|
-
description: "Record or resolve one commitment such as a promise or agreement.",
|
|
6657
|
+
description: "Record or resolve one commitment such as a promise or agreement. The owner contact is auto-injected into participants if missing.",
|
|
6468
6658
|
whenToUse: "Use this when the work is specifically about a commitment lifecycle rather than a general event.",
|
|
6469
6659
|
whenNotToUse: "Do not use this for ordinary interactions or direct relationship state changes.",
|
|
6470
6660
|
sideEffects: "writes_state",
|
|
@@ -6482,10 +6672,42 @@ var manageCommitmentTool = defineAffinityTool({
|
|
|
6482
6672
|
inputSchema: objectSchema(
|
|
6483
6673
|
{
|
|
6484
6674
|
action: enumSchema("Operation to perform.", ["record", "resolve"]),
|
|
6485
|
-
input:
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6675
|
+
input: objectSchema(
|
|
6676
|
+
{
|
|
6677
|
+
commitmentType: enumSchema("Promise or agreement.", [
|
|
6678
|
+
"promise",
|
|
6679
|
+
"agreement"
|
|
6680
|
+
]),
|
|
6681
|
+
occurredAt: integerSchema(
|
|
6682
|
+
"When the commitment was made, Unix timestamp in milliseconds."
|
|
6683
|
+
),
|
|
6684
|
+
summary: stringSchema("Commitment summary. Must be non-empty."),
|
|
6685
|
+
significance: integerSchema("Commitment significance from 1 to 10."),
|
|
6686
|
+
dueAt: integerSchema("Optional due timestamp."),
|
|
6687
|
+
participants: arraySchema(
|
|
6688
|
+
objectSchema(
|
|
6689
|
+
{
|
|
6690
|
+
contactId: integerSchema(
|
|
6691
|
+
"Participant contact id. The owner contact MUST be included."
|
|
6692
|
+
),
|
|
6693
|
+
role: stringSchema("Participant role."),
|
|
6694
|
+
directionality: stringSchema("Optional directionality.")
|
|
6695
|
+
},
|
|
6696
|
+
["contactId", "role"]
|
|
6697
|
+
),
|
|
6698
|
+
"Participant list. Must be non-empty and include the owner contact."
|
|
6699
|
+
),
|
|
6700
|
+
now: integerSchema("Optional timestamp override.")
|
|
6701
|
+
},
|
|
6702
|
+
[
|
|
6703
|
+
"commitmentType",
|
|
6704
|
+
"occurredAt",
|
|
6705
|
+
"summary",
|
|
6706
|
+
"significance",
|
|
6707
|
+
"participants"
|
|
6708
|
+
],
|
|
6709
|
+
"Commitment payload. Required when action=record."
|
|
6710
|
+
),
|
|
6489
6711
|
commitmentEventId: integerSchema(
|
|
6490
6712
|
"Commitment event id. Required when action=resolve."
|
|
6491
6713
|
),
|
|
@@ -6641,7 +6863,124 @@ var manageContactTool = defineAffinityTool({
|
|
|
6641
6863
|
handler: manageContactToolHandler
|
|
6642
6864
|
});
|
|
6643
6865
|
|
|
6866
|
+
// src/tools/manage_duplicate_dismissal_tool.ts
|
|
6867
|
+
function contactLocatorSchema4(description) {
|
|
6868
|
+
return {
|
|
6869
|
+
type: "object",
|
|
6870
|
+
properties: {
|
|
6871
|
+
contactId: integerSchema("Exact contact id."),
|
|
6872
|
+
identity: objectSchema(
|
|
6873
|
+
{
|
|
6874
|
+
type: stringSchema("Identity type."),
|
|
6875
|
+
value: stringSchema("Identity value.")
|
|
6876
|
+
},
|
|
6877
|
+
["type", "value"],
|
|
6878
|
+
"Contact identity locator."
|
|
6879
|
+
)
|
|
6880
|
+
},
|
|
6881
|
+
description
|
|
6882
|
+
};
|
|
6883
|
+
}
|
|
6884
|
+
function manageDuplicateDismissalToolHandler(db, input) {
|
|
6885
|
+
return withToolHandling(() => {
|
|
6886
|
+
const left = resolveContactLocator(db, input.left, "left");
|
|
6887
|
+
if (!left.ok) {
|
|
6888
|
+
return left.result;
|
|
6889
|
+
}
|
|
6890
|
+
const right = resolveContactLocator(db, input.right, "right");
|
|
6891
|
+
if (!right.ok) {
|
|
6892
|
+
return right.result;
|
|
6893
|
+
}
|
|
6894
|
+
if (input.action === "dismiss") {
|
|
6895
|
+
const receipt2 = dismissDuplicate(
|
|
6896
|
+
db,
|
|
6897
|
+
left.value.id,
|
|
6898
|
+
right.value.id,
|
|
6899
|
+
input.reason ?? null
|
|
6900
|
+
);
|
|
6901
|
+
return toolSuccess(
|
|
6902
|
+
`Dismissed pair #${receipt2.primary.leftContactId} / #${receipt2.primary.rightContactId}.`,
|
|
6903
|
+
{ action: "dismiss", primary: receipt2.primary }
|
|
6904
|
+
);
|
|
6905
|
+
}
|
|
6906
|
+
const receipt = undismissDuplicate(db, left.value.id, right.value.id);
|
|
6907
|
+
return toolSuccess(
|
|
6908
|
+
`Un-dismissed pair #${receipt.primary.leftContactId} / #${receipt.primary.rightContactId}.`,
|
|
6909
|
+
{ action: "undismiss", primary: receipt.primary }
|
|
6910
|
+
);
|
|
6911
|
+
}, "Duplicate dismissal failed.");
|
|
6912
|
+
}
|
|
6913
|
+
var manageDuplicateDismissalTool = defineAffinityTool({
|
|
6914
|
+
name: manageDuplicateDismissalToolName,
|
|
6915
|
+
description: "Dismiss or un-dismiss a duplicate candidate pair so it does not resurface in listDuplicateCandidates.",
|
|
6916
|
+
whenToUse: "Use dismiss after confirming two contacts are different people (not duplicates). Use undismiss to re-surface a pair for re-evaluation.",
|
|
6917
|
+
whenNotToUse: "Do not use this to merge contacts \u2014 use merge_contacts for confirmed duplicates.",
|
|
6918
|
+
sideEffects: "writes_state",
|
|
6919
|
+
readOnly: false,
|
|
6920
|
+
supportsClarification: true,
|
|
6921
|
+
targetKinds: ["contact"],
|
|
6922
|
+
inputDescriptions: {
|
|
6923
|
+
action: "dismiss to suppress the pair; undismiss to re-surface it.",
|
|
6924
|
+
left: "One contact of the pair.",
|
|
6925
|
+
right: "The other contact of the pair.",
|
|
6926
|
+
reason: "Optional reason for the dismissal (audit trail)."
|
|
6927
|
+
},
|
|
6928
|
+
outputDescription: "Returns the dismissal receipt with the canonical pair identifiers.",
|
|
6929
|
+
inputSchema: objectSchema(
|
|
6930
|
+
{
|
|
6931
|
+
action: enumSchema("Action to perform.", ["dismiss", "undismiss"]),
|
|
6932
|
+
left: contactLocatorSchema4(
|
|
6933
|
+
"Left contact. Provide contactId or identity."
|
|
6934
|
+
),
|
|
6935
|
+
right: contactLocatorSchema4(
|
|
6936
|
+
"Right contact. Provide contactId or identity."
|
|
6937
|
+
),
|
|
6938
|
+
reason: nullableStringSchema("Optional reason for the dismissal.")
|
|
6939
|
+
},
|
|
6940
|
+
["action", "left", "right"],
|
|
6941
|
+
"Dismiss or un-dismiss a duplicate candidate pair."
|
|
6942
|
+
),
|
|
6943
|
+
handler: manageDuplicateDismissalToolHandler
|
|
6944
|
+
});
|
|
6945
|
+
|
|
6644
6946
|
// src/tools/manage_date_anchor_tool.ts
|
|
6947
|
+
function contactLocatorSchema5(description) {
|
|
6948
|
+
return {
|
|
6949
|
+
type: "object",
|
|
6950
|
+
properties: {
|
|
6951
|
+
contactId: integerSchema("Exact contact id."),
|
|
6952
|
+
identity: objectSchema(
|
|
6953
|
+
{
|
|
6954
|
+
type: stringSchema("Identity type."),
|
|
6955
|
+
value: stringSchema("Identity value.")
|
|
6956
|
+
},
|
|
6957
|
+
["type", "value"],
|
|
6958
|
+
"Contact identity locator."
|
|
6959
|
+
)
|
|
6960
|
+
},
|
|
6961
|
+
description
|
|
6962
|
+
};
|
|
6963
|
+
}
|
|
6964
|
+
function linkLocatorSchema3(description) {
|
|
6965
|
+
return {
|
|
6966
|
+
type: "object",
|
|
6967
|
+
properties: {
|
|
6968
|
+
linkId: integerSchema("Exact link id."),
|
|
6969
|
+
endpoints: objectSchema(
|
|
6970
|
+
{
|
|
6971
|
+
fromContactId: integerSchema("From contact id."),
|
|
6972
|
+
toContactId: integerSchema("To contact id."),
|
|
6973
|
+
kind: stringSchema("Optional link kind."),
|
|
6974
|
+
role: stringSchema("Optional link role."),
|
|
6975
|
+
isStructural: booleanSchema("Optional structural discriminator.")
|
|
6976
|
+
},
|
|
6977
|
+
["fromContactId", "toContactId"],
|
|
6978
|
+
"Endpoint-based link locator."
|
|
6979
|
+
)
|
|
6980
|
+
},
|
|
6981
|
+
description
|
|
6982
|
+
};
|
|
6983
|
+
}
|
|
6645
6984
|
function resolveDateAnchorTarget(db, target) {
|
|
6646
6985
|
if (target.kind === "contact") {
|
|
6647
6986
|
const contact = resolveContactLocator(db, target.contact, "target.contact");
|
|
@@ -6707,17 +7046,65 @@ var manageDateAnchorTool = defineAffinityTool({
|
|
|
6707
7046
|
inputSchema: objectSchema(
|
|
6708
7047
|
{
|
|
6709
7048
|
action: enumSchema("Operation to perform.", ["add", "revise", "remove"]),
|
|
6710
|
-
input:
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
7049
|
+
input: objectSchema(
|
|
7050
|
+
{
|
|
7051
|
+
target: objectSchema(
|
|
7052
|
+
{
|
|
7053
|
+
kind: enumSchema("Target kind.", ["contact", "link"]),
|
|
7054
|
+
contact: contactLocatorSchema5(
|
|
7055
|
+
"Contact locator. Required when kind=contact. Provide contactId or identity."
|
|
7056
|
+
),
|
|
7057
|
+
link: linkLocatorSchema3(
|
|
7058
|
+
"Link locator. Required when kind=link. Provide linkId or endpoints."
|
|
7059
|
+
)
|
|
7060
|
+
},
|
|
7061
|
+
["kind"],
|
|
7062
|
+
"Date-anchor target."
|
|
7063
|
+
),
|
|
7064
|
+
recurrenceKind: enumSchema("Recurrence kind.", [
|
|
7065
|
+
"birthday",
|
|
7066
|
+
"anniversary",
|
|
7067
|
+
"renewal",
|
|
7068
|
+
"memorial",
|
|
7069
|
+
"custom_yearly"
|
|
7070
|
+
]),
|
|
7071
|
+
anchorMonth: integerSchema("Anchor month, 1\u201312."),
|
|
7072
|
+
anchorDay: integerSchema("Anchor day, 1\u201331 (validated per month)."),
|
|
7073
|
+
summary: stringSchema("Summary. Must be non-empty."),
|
|
7074
|
+
significance: integerSchema("Significance from 1 to 10."),
|
|
7075
|
+
now: integerSchema("Optional timestamp override."),
|
|
7076
|
+
force: booleanSchema("Whether to bypass duplicate checks.")
|
|
7077
|
+
},
|
|
7078
|
+
[
|
|
7079
|
+
"target",
|
|
7080
|
+
"recurrenceKind",
|
|
7081
|
+
"anchorMonth",
|
|
7082
|
+
"anchorDay",
|
|
7083
|
+
"summary",
|
|
7084
|
+
"significance"
|
|
7085
|
+
],
|
|
7086
|
+
"Date anchor payload. Required when action=add."
|
|
7087
|
+
),
|
|
6714
7088
|
anchorEventId: integerSchema(
|
|
6715
7089
|
"Anchor event id. Required when action=revise or remove."
|
|
6716
7090
|
),
|
|
6717
|
-
patch:
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
7091
|
+
patch: objectSchema(
|
|
7092
|
+
{
|
|
7093
|
+
recurrenceKind: enumSchema("Optional recurrence kind.", [
|
|
7094
|
+
"birthday",
|
|
7095
|
+
"anniversary",
|
|
7096
|
+
"renewal",
|
|
7097
|
+
"memorial",
|
|
7098
|
+
"custom_yearly"
|
|
7099
|
+
]),
|
|
7100
|
+
anchorMonth: integerSchema("Optional month, 1\u201312."),
|
|
7101
|
+
anchorDay: integerSchema("Optional day, 1\u201331."),
|
|
7102
|
+
summary: stringSchema("Optional summary."),
|
|
7103
|
+
significance: integerSchema("Optional significance, 1\u201310.")
|
|
7104
|
+
},
|
|
7105
|
+
[],
|
|
7106
|
+
"Anchor patch. Required when action=revise."
|
|
7107
|
+
),
|
|
6721
7108
|
options: objectSchema(
|
|
6722
7109
|
{
|
|
6723
7110
|
now: integerSchema("Optional timestamp."),
|
|
@@ -6818,6 +7205,9 @@ function addIdentity(db, contactId, input) {
|
|
|
6818
7205
|
throw new AffinityInvariantError("inserted identity not found");
|
|
6819
7206
|
}
|
|
6820
7207
|
refreshContactRollup(db, contactId, now);
|
|
7208
|
+
db.prepare(
|
|
7209
|
+
"DELETE FROM dismissed_duplicates WHERE left_id = ? OR right_id = ?"
|
|
7210
|
+
).run(contactId, contactId);
|
|
6821
7211
|
return buildIdentityMutationReceipt(mapIdentityRowToIdentityRecord(row), {
|
|
6822
7212
|
created: [{ kind: "identity", id }]
|
|
6823
7213
|
});
|
|
@@ -6841,6 +7231,9 @@ function removeIdentity(db, identityId, removedAt) {
|
|
|
6841
7231
|
"UPDATE identities SET removed_at = ?, updated_at = ? WHERE id = ?"
|
|
6842
7232
|
).run(now, now, identityId);
|
|
6843
7233
|
refreshContactRollup(db, row.contact_id, now);
|
|
7234
|
+
db.prepare(
|
|
7235
|
+
"DELETE FROM dismissed_duplicates WHERE left_id = ? OR right_id = ?"
|
|
7236
|
+
).run(row.contact_id, row.contact_id);
|
|
6844
7237
|
return buildIdentityMutationReceipt(primary, {
|
|
6845
7238
|
removed: [{ kind: "identity", id: identityId }]
|
|
6846
7239
|
});
|
|
@@ -6919,7 +7312,7 @@ function verifyIdentity(db, identityId, verifiedAt) {
|
|
|
6919
7312
|
}
|
|
6920
7313
|
|
|
6921
7314
|
// src/tools/manage_identity_tool.ts
|
|
6922
|
-
function
|
|
7315
|
+
function contactLocatorSchema6(description) {
|
|
6923
7316
|
return {
|
|
6924
7317
|
type: "object",
|
|
6925
7318
|
properties: {
|
|
@@ -6998,7 +7391,7 @@ var manageIdentityTool = defineAffinityTool({
|
|
|
6998
7391
|
"verify",
|
|
6999
7392
|
"remove"
|
|
7000
7393
|
]),
|
|
7001
|
-
contact:
|
|
7394
|
+
contact: contactLocatorSchema6(
|
|
7002
7395
|
"Target contact. Required when action=add. Provide contactId or identity."
|
|
7003
7396
|
),
|
|
7004
7397
|
input: objectSchema(
|
|
@@ -7038,7 +7431,7 @@ var manageIdentityTool = defineAffinityTool({
|
|
|
7038
7431
|
});
|
|
7039
7432
|
|
|
7040
7433
|
// src/tools/manage_relationship_tool.ts
|
|
7041
|
-
function
|
|
7434
|
+
function contactLocatorSchema7(description) {
|
|
7042
7435
|
return {
|
|
7043
7436
|
type: "object",
|
|
7044
7437
|
properties: {
|
|
@@ -7055,7 +7448,7 @@ function contactLocatorSchema5(description) {
|
|
|
7055
7448
|
description
|
|
7056
7449
|
};
|
|
7057
7450
|
}
|
|
7058
|
-
function
|
|
7451
|
+
function linkLocatorSchema4(description) {
|
|
7059
7452
|
return {
|
|
7060
7453
|
type: "object",
|
|
7061
7454
|
properties: {
|
|
@@ -7194,14 +7587,14 @@ var manageRelationshipTool = defineAffinityTool({
|
|
|
7194
7587
|
"set_structural_tie",
|
|
7195
7588
|
"remove_structural_tie"
|
|
7196
7589
|
]),
|
|
7197
|
-
from:
|
|
7590
|
+
from: contactLocatorSchema7(
|
|
7198
7591
|
"Source contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity."
|
|
7199
7592
|
),
|
|
7200
|
-
to:
|
|
7593
|
+
to: contactLocatorSchema7(
|
|
7201
7594
|
"Target contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity."
|
|
7202
7595
|
),
|
|
7203
7596
|
kind: stringSchema(
|
|
7204
|
-
"Link kind. Required for seed_social_link and set_structural_tie. Can be passed here or inside input."
|
|
7597
|
+
"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."
|
|
7205
7598
|
),
|
|
7206
7599
|
role: stringSchema(
|
|
7207
7600
|
"Optional role for seed_social_link or set_structural_tie. Can be passed here or inside input."
|
|
@@ -7210,7 +7603,7 @@ var manageRelationshipTool = defineAffinityTool({
|
|
|
7210
7603
|
type: "object",
|
|
7211
7604
|
description: "Full payload for seed_social_link or set_structural_tie. Alternative to top-level kind/role."
|
|
7212
7605
|
},
|
|
7213
|
-
link:
|
|
7606
|
+
link: linkLocatorSchema4(
|
|
7214
7607
|
"Target link. Required when action=revise_bond, override_state, or remove_structural_tie. Provide linkId or endpoints."
|
|
7215
7608
|
),
|
|
7216
7609
|
bond: nullableStringSchema(
|
|
@@ -7239,7 +7632,7 @@ var manageRelationshipTool = defineAffinityTool({
|
|
|
7239
7632
|
});
|
|
7240
7633
|
|
|
7241
7634
|
// src/tools/merge_contacts_tool.ts
|
|
7242
|
-
function
|
|
7635
|
+
function contactLocatorSchema8(description) {
|
|
7243
7636
|
return {
|
|
7244
7637
|
type: "object",
|
|
7245
7638
|
properties: {
|
|
@@ -7296,10 +7689,10 @@ var mergeContactsTool = defineAffinityTool({
|
|
|
7296
7689
|
outputDescription: "Returns the merge receipt with rewired entities and affected-link information.",
|
|
7297
7690
|
inputSchema: objectSchema(
|
|
7298
7691
|
{
|
|
7299
|
-
winner:
|
|
7692
|
+
winner: contactLocatorSchema8(
|
|
7300
7693
|
"Winner contact. Provide contactId or identity."
|
|
7301
7694
|
),
|
|
7302
|
-
loser:
|
|
7695
|
+
loser: contactLocatorSchema8(
|
|
7303
7696
|
"Loser contact. Provide contactId or identity."
|
|
7304
7697
|
),
|
|
7305
7698
|
reasonSummary: stringSchema("Optional merge reason."),
|
|
@@ -7312,31 +7705,64 @@ var mergeContactsTool = defineAffinityTool({
|
|
|
7312
7705
|
});
|
|
7313
7706
|
|
|
7314
7707
|
// src/tools/record_event_tool.ts
|
|
7708
|
+
function normalizeParticipant2(p) {
|
|
7709
|
+
let contactId = p.contactId;
|
|
7710
|
+
if (contactId === void 0 && p.contact != null) {
|
|
7711
|
+
const loc = p.contact;
|
|
7712
|
+
contactId = loc.contactId;
|
|
7713
|
+
}
|
|
7714
|
+
const result = {
|
|
7715
|
+
contactId,
|
|
7716
|
+
role: p.role
|
|
7717
|
+
};
|
|
7718
|
+
if (p.directionality !== void 0) {
|
|
7719
|
+
result.directionality = p.directionality;
|
|
7720
|
+
}
|
|
7721
|
+
return result;
|
|
7722
|
+
}
|
|
7723
|
+
function ensureOwnerParticipates2(db, participants) {
|
|
7724
|
+
const ownerId = findOwnerContactId(db);
|
|
7725
|
+
if (ownerId === null) return participants;
|
|
7726
|
+
if (participants.some((p) => p.contactId === ownerId)) return participants;
|
|
7727
|
+
return [{ contactId: ownerId, role: "actor" }, ...participants];
|
|
7728
|
+
}
|
|
7729
|
+
function prepareParticipants2(db, raw, injectOwner) {
|
|
7730
|
+
const normalized = raw.map(normalizeParticipant2);
|
|
7731
|
+
return injectOwner ? ensureOwnerParticipates2(db, normalized) : normalized;
|
|
7732
|
+
}
|
|
7315
7733
|
function recordEventToolHandler(db, input) {
|
|
7316
7734
|
return withToolHandling(() => {
|
|
7735
|
+
const patched = {
|
|
7736
|
+
...input.input,
|
|
7737
|
+
participants: prepareParticipants2(
|
|
7738
|
+
db,
|
|
7739
|
+
input.input.participants,
|
|
7740
|
+
input.kind !== "observation"
|
|
7741
|
+
)
|
|
7742
|
+
};
|
|
7317
7743
|
switch (input.kind) {
|
|
7318
7744
|
case "interaction":
|
|
7319
7745
|
return mutationToolResult(
|
|
7320
7746
|
"interaction",
|
|
7321
|
-
recordInteraction(db,
|
|
7747
|
+
recordInteraction(db, patched),
|
|
7322
7748
|
"event"
|
|
7323
7749
|
);
|
|
7324
7750
|
case "observation":
|
|
7325
7751
|
return mutationToolResult(
|
|
7326
7752
|
"observation",
|
|
7327
|
-
recordObservation(db,
|
|
7753
|
+
recordObservation(db, patched),
|
|
7328
7754
|
"event"
|
|
7329
7755
|
);
|
|
7330
7756
|
case "milestone":
|
|
7331
7757
|
return mutationToolResult(
|
|
7332
7758
|
"milestone",
|
|
7333
|
-
recordMilestone(db,
|
|
7759
|
+
recordMilestone(db, patched),
|
|
7334
7760
|
"event"
|
|
7335
7761
|
);
|
|
7336
7762
|
case "transaction":
|
|
7337
7763
|
return mutationToolResult(
|
|
7338
7764
|
"transaction",
|
|
7339
|
-
recordTransaction(db,
|
|
7765
|
+
recordTransaction(db, patched),
|
|
7340
7766
|
"event"
|
|
7341
7767
|
);
|
|
7342
7768
|
}
|
|
@@ -7344,7 +7770,7 @@ function recordEventToolHandler(db, input) {
|
|
|
7344
7770
|
}
|
|
7345
7771
|
var recordEventTool = defineAffinityTool({
|
|
7346
7772
|
name: recordEventToolName,
|
|
7347
|
-
description: "Record one social event such as an interaction, observation, milestone, or transaction.",
|
|
7773
|
+
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.",
|
|
7348
7774
|
whenToUse: "Use this when you are capturing something that happened in the relationship graph.",
|
|
7349
7775
|
whenNotToUse: "Do not use this for commitments, date anchors, or direct relationship state overrides.",
|
|
7350
7776
|
sideEffects: "writes_state",
|
|
@@ -7364,10 +7790,53 @@ var recordEventTool = defineAffinityTool({
|
|
|
7364
7790
|
"milestone",
|
|
7365
7791
|
"transaction"
|
|
7366
7792
|
]),
|
|
7367
|
-
input:
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7793
|
+
input: objectSchema(
|
|
7794
|
+
{
|
|
7795
|
+
type: enumSchema(
|
|
7796
|
+
"Interaction subtype. Required when kind=interaction, omit otherwise.",
|
|
7797
|
+
[
|
|
7798
|
+
"conversation",
|
|
7799
|
+
"activity",
|
|
7800
|
+
"gift",
|
|
7801
|
+
"support",
|
|
7802
|
+
"conflict",
|
|
7803
|
+
"correction"
|
|
7804
|
+
]
|
|
7805
|
+
),
|
|
7806
|
+
occurredAt: integerSchema(
|
|
7807
|
+
"When the event occurred, Unix timestamp in milliseconds."
|
|
7808
|
+
),
|
|
7809
|
+
summary: stringSchema("Event summary. Must be non-empty."),
|
|
7810
|
+
significance: integerSchema("Event significance from 1 to 10."),
|
|
7811
|
+
participants: arraySchema(
|
|
7812
|
+
objectSchema(
|
|
7813
|
+
{
|
|
7814
|
+
contactId: integerSchema(
|
|
7815
|
+
"Participant contact id. The owner contact MUST be included."
|
|
7816
|
+
),
|
|
7817
|
+
role: enumSchema("Participant role.", [
|
|
7818
|
+
"actor",
|
|
7819
|
+
"recipient",
|
|
7820
|
+
"subject",
|
|
7821
|
+
"observer",
|
|
7822
|
+
"mentioned"
|
|
7823
|
+
]),
|
|
7824
|
+
directionality: enumSchema("Optional directionality.", [
|
|
7825
|
+
"owner_initiated",
|
|
7826
|
+
"other_initiated",
|
|
7827
|
+
"mutual",
|
|
7828
|
+
"observed"
|
|
7829
|
+
])
|
|
7830
|
+
},
|
|
7831
|
+
["contactId", "role"]
|
|
7832
|
+
),
|
|
7833
|
+
"Participant list. Must be non-empty and include the owner contact."
|
|
7834
|
+
),
|
|
7835
|
+
now: integerSchema("Optional timestamp override.")
|
|
7836
|
+
},
|
|
7837
|
+
["occurredAt", "summary", "significance", "participants"],
|
|
7838
|
+
"Event payload. Always required. Interaction events also require `type`."
|
|
7839
|
+
)
|
|
7371
7840
|
},
|
|
7372
7841
|
["kind", "input"],
|
|
7373
7842
|
"Record one social event."
|
|
@@ -7394,7 +7863,7 @@ function buildChartOptions(input) {
|
|
|
7394
7863
|
...input.contactIds === void 0 ? {} : { contactIds: input.contactIds }
|
|
7395
7864
|
};
|
|
7396
7865
|
}
|
|
7397
|
-
function
|
|
7866
|
+
function contactLocatorSchema9(description) {
|
|
7398
7867
|
return {
|
|
7399
7868
|
type: "object",
|
|
7400
7869
|
properties: {
|
|
@@ -7411,7 +7880,7 @@ function contactLocatorSchema7(description) {
|
|
|
7411
7880
|
description
|
|
7412
7881
|
};
|
|
7413
7882
|
}
|
|
7414
|
-
function
|
|
7883
|
+
function linkLocatorSchema5(description) {
|
|
7415
7884
|
return {
|
|
7416
7885
|
type: "object",
|
|
7417
7886
|
properties: {
|
|
@@ -7485,6 +7954,14 @@ function validateReviewInput(input) {
|
|
|
7485
7954
|
"Use `contactIds` only with duplicate-candidate or chart review views."
|
|
7486
7955
|
);
|
|
7487
7956
|
}
|
|
7957
|
+
if (input.includeDismissed !== void 0 && input.view !== "contacts.duplicates") {
|
|
7958
|
+
return toolFailure(
|
|
7959
|
+
"protocol",
|
|
7960
|
+
"invalid_input",
|
|
7961
|
+
"The `includeDismissed` filter is not valid for this review view.",
|
|
7962
|
+
"Use `includeDismissed` only with the contacts.duplicates review view."
|
|
7963
|
+
);
|
|
7964
|
+
}
|
|
7488
7965
|
return null;
|
|
7489
7966
|
}
|
|
7490
7967
|
function duplicateCandidateItem(candidate) {
|
|
@@ -7541,6 +8018,15 @@ function mergeHistoryItem(record) {
|
|
|
7541
8018
|
snippet: record.reasonSummary ?? void 0
|
|
7542
8019
|
};
|
|
7543
8020
|
}
|
|
8021
|
+
function dismissedPairItem(record) {
|
|
8022
|
+
return {
|
|
8023
|
+
kind: "contact",
|
|
8024
|
+
id: record.leftContactId,
|
|
8025
|
+
title: `Dismissed pair: #${record.leftContactId} and #${record.rightContactId}`,
|
|
8026
|
+
subtitle: record.reason ?? "no reason given",
|
|
8027
|
+
snippet: `dismissed at ${record.dismissedAt}`
|
|
8028
|
+
};
|
|
8029
|
+
}
|
|
7544
8030
|
function compactFilters(input) {
|
|
7545
8031
|
return Object.fromEntries(
|
|
7546
8032
|
Object.entries(input).filter(
|
|
@@ -7576,7 +8062,8 @@ function reviewAffinityToolHandler(db, input) {
|
|
|
7576
8062
|
{
|
|
7577
8063
|
...input.contactIds === void 0 ? {} : { contactIds: input.contactIds },
|
|
7578
8064
|
...input.exactOnly === void 0 ? {} : { exactOnly: input.exactOnly },
|
|
7579
|
-
...input.minScore === void 0 ? {} : { minScore: input.minScore }
|
|
8065
|
+
...input.minScore === void 0 ? {} : { minScore: input.minScore },
|
|
8066
|
+
...input.includeDismissed === void 0 ? {} : { includeDismissed: input.includeDismissed }
|
|
7580
8067
|
},
|
|
7581
8068
|
options
|
|
7582
8069
|
).map(duplicateCandidateItem);
|
|
@@ -7757,6 +8244,9 @@ function reviewAffinityToolHandler(db, input) {
|
|
|
7757
8244
|
);
|
|
7758
8245
|
break;
|
|
7759
8246
|
}
|
|
8247
|
+
case "merges.dismissed":
|
|
8248
|
+
items = listDismissedDuplicates(db, options).map(dismissedPairItem);
|
|
8249
|
+
break;
|
|
7760
8250
|
}
|
|
7761
8251
|
return toolSuccess(
|
|
7762
8252
|
summarizeCount(items.length, "item"),
|
|
@@ -7807,6 +8297,7 @@ var reviewAffinityTool = defineAffinityTool({
|
|
|
7807
8297
|
horizonDays: "Optional horizon filter for commitments or upcoming dates.",
|
|
7808
8298
|
exactOnly: "Whether duplicate review should only include exact identity matches.",
|
|
7809
8299
|
minScore: "Optional minimum duplicate score.",
|
|
8300
|
+
includeDismissed: "Whether dismissed pairs should be included in duplicate review (marked with dismissed flag).",
|
|
7810
8301
|
since: "Optional lower timestamp bound for time-based views.",
|
|
7811
8302
|
until: "Optional upper timestamp bound for time-based views.",
|
|
7812
8303
|
limit: "Optional result limit.",
|
|
@@ -7828,10 +8319,11 @@ var reviewAffinityTool = defineAffinityTool({
|
|
|
7828
8319
|
"links.radar",
|
|
7829
8320
|
"dates.upcoming",
|
|
7830
8321
|
"graph.chart",
|
|
7831
|
-
"merges.history"
|
|
8322
|
+
"merges.history",
|
|
8323
|
+
"merges.dismissed"
|
|
7832
8324
|
]),
|
|
7833
|
-
contact:
|
|
7834
|
-
link:
|
|
8325
|
+
contact: contactLocatorSchema9("Optional target contact locator."),
|
|
8326
|
+
link: linkLocatorSchema5("Optional target link locator."),
|
|
7835
8327
|
contactIds: arraySchema(
|
|
7836
8328
|
integerSchema("Contact id."),
|
|
7837
8329
|
"Optional contact-id subset."
|
|
@@ -7881,6 +8373,9 @@ var reviewAffinityTool = defineAffinityTool({
|
|
|
7881
8373
|
horizonDays: integerSchema("Optional horizon-days filter."),
|
|
7882
8374
|
exactOnly: booleanSchema("Whether duplicate review is exact-only."),
|
|
7883
8375
|
minScore: numberSchema("Optional duplicate minimum score."),
|
|
8376
|
+
includeDismissed: booleanSchema(
|
|
8377
|
+
"Whether dismissed pairs are included in duplicate review (marked with dismissed flag)."
|
|
8378
|
+
),
|
|
7884
8379
|
since: integerSchema("Optional lower timestamp bound."),
|
|
7885
8380
|
until: integerSchema("Optional upper timestamp bound."),
|
|
7886
8381
|
limit: integerSchema("Optional result limit."),
|
|
@@ -7979,6 +8474,7 @@ var affinityTools = [
|
|
|
7979
8474
|
inspectAffinityItemTool,
|
|
7980
8475
|
manageContactTool,
|
|
7981
8476
|
mergeContactsTool,
|
|
8477
|
+
manageDuplicateDismissalTool,
|
|
7982
8478
|
manageIdentityTool,
|
|
7983
8479
|
manageRelationshipTool,
|
|
7984
8480
|
recordEventTool,
|
|
@@ -8080,6 +8576,21 @@ var affinityToolMappings = [
|
|
|
8080
8576
|
action: "set_lifecycle"
|
|
8081
8577
|
},
|
|
8082
8578
|
{ source: "mergeContacts", tool: mergeContactsToolName },
|
|
8579
|
+
{
|
|
8580
|
+
source: "dismissDuplicate",
|
|
8581
|
+
tool: manageDuplicateDismissalToolName,
|
|
8582
|
+
action: "dismiss"
|
|
8583
|
+
},
|
|
8584
|
+
{
|
|
8585
|
+
source: "undismissDuplicate",
|
|
8586
|
+
tool: manageDuplicateDismissalToolName,
|
|
8587
|
+
action: "undismiss"
|
|
8588
|
+
},
|
|
8589
|
+
{
|
|
8590
|
+
source: "listDismissedDuplicates",
|
|
8591
|
+
tool: reviewAffinityToolName,
|
|
8592
|
+
view: "merges.dismissed"
|
|
8593
|
+
},
|
|
8083
8594
|
{ source: "addIdentity", tool: manageIdentityToolName, action: "add" },
|
|
8084
8595
|
{ source: "reviseIdentity", tool: manageIdentityToolName, action: "revise" },
|
|
8085
8596
|
{ source: "verifyIdentity", tool: manageIdentityToolName, action: "verify" },
|
|
@@ -8161,6 +8672,7 @@ __export(write_exports, {
|
|
|
8161
8672
|
addDateAnchor: () => addDateAnchor,
|
|
8162
8673
|
addIdentity: () => addIdentity,
|
|
8163
8674
|
createContact: () => createContact,
|
|
8675
|
+
dismissDuplicate: () => dismissDuplicate,
|
|
8164
8676
|
mergeContacts: () => mergeContacts,
|
|
8165
8677
|
overrideLinkState: () => overrideLinkState,
|
|
8166
8678
|
recordCommitment: () => recordCommitment,
|
|
@@ -8181,6 +8693,7 @@ __export(write_exports, {
|
|
|
8181
8693
|
setAttribute: () => setAttribute,
|
|
8182
8694
|
setContactLifecycle: () => setContactLifecycle,
|
|
8183
8695
|
setStructuralTie: () => setStructuralTie,
|
|
8696
|
+
undismissDuplicate: () => undismissDuplicate,
|
|
8184
8697
|
unsetAttribute: () => unsetAttribute,
|
|
8185
8698
|
verifyIdentity: () => verifyIdentity
|
|
8186
8699
|
});
|