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