@ouro.bot/friends 0.1.0-alpha.4

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.
Files changed (82) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +514 -0
  3. package/changelog.json +34 -0
  4. package/dist/a2a/index.d.ts +102 -0
  5. package/dist/a2a/index.js +198 -0
  6. package/dist/agent-peer.d.ts +17 -0
  7. package/dist/agent-peer.js +57 -0
  8. package/dist/channel.d.ts +11 -0
  9. package/dist/channel.js +132 -0
  10. package/dist/consent.d.ts +34 -0
  11. package/dist/consent.js +62 -0
  12. package/dist/coordination.d.ts +100 -0
  13. package/dist/coordination.js +255 -0
  14. package/dist/file-bundle.d.ts +12 -0
  15. package/dist/file-bundle.js +23 -0
  16. package/dist/grant-store-file.d.ts +16 -0
  17. package/dist/grant-store-file.js +136 -0
  18. package/dist/grant-store.d.ts +7 -0
  19. package/dist/grant-store.js +8 -0
  20. package/dist/grants.d.ts +39 -0
  21. package/dist/grants.js +84 -0
  22. package/dist/group-context.d.ts +21 -0
  23. package/dist/group-context.js +144 -0
  24. package/dist/index.d.ts +49 -0
  25. package/dist/index.js +105 -0
  26. package/dist/link-identity.d.ts +14 -0
  27. package/dist/link-identity.js +88 -0
  28. package/dist/mcp/bin.d.ts +2 -0
  29. package/dist/mcp/bin.js +16 -0
  30. package/dist/mcp/dispatch.d.ts +14 -0
  31. package/dist/mcp/dispatch.js +432 -0
  32. package/dist/mcp/index.d.ts +6 -0
  33. package/dist/mcp/index.js +14 -0
  34. package/dist/mcp/run-main.d.ts +7 -0
  35. package/dist/mcp/run-main.js +45 -0
  36. package/dist/mcp/schemas.d.ts +10 -0
  37. package/dist/mcp/schemas.js +398 -0
  38. package/dist/mcp/server.d.ts +21 -0
  39. package/dist/mcp/server.js +194 -0
  40. package/dist/mission-share.d.ts +94 -0
  41. package/dist/mission-share.js +232 -0
  42. package/dist/mission-store-file.d.ts +18 -0
  43. package/dist/mission-store-file.js +153 -0
  44. package/dist/mission-store.d.ts +10 -0
  45. package/dist/mission-store.js +9 -0
  46. package/dist/missions.d.ts +31 -0
  47. package/dist/missions.js +98 -0
  48. package/dist/notes.d.ts +11 -0
  49. package/dist/notes.js +90 -0
  50. package/dist/observability.d.ts +27 -0
  51. package/dist/observability.js +31 -0
  52. package/dist/outcomes.d.ts +9 -0
  53. package/dist/outcomes.js +51 -0
  54. package/dist/resolver.d.ts +28 -0
  55. package/dist/resolver.js +187 -0
  56. package/dist/results.d.ts +8 -0
  57. package/dist/results.js +2 -0
  58. package/dist/room.d.ts +22 -0
  59. package/dist/room.js +40 -0
  60. package/dist/share.d.ts +106 -0
  61. package/dist/share.js +223 -0
  62. package/dist/standing.d.ts +83 -0
  63. package/dist/standing.js +111 -0
  64. package/dist/store-file.d.ts +21 -0
  65. package/dist/store-file.js +264 -0
  66. package/dist/store.d.ts +9 -0
  67. package/dist/store.js +4 -0
  68. package/dist/tokens.d.ts +8 -0
  69. package/dist/tokens.js +26 -0
  70. package/dist/trust-explanation.d.ts +16 -0
  71. package/dist/trust-explanation.js +74 -0
  72. package/dist/trust-mutation.d.ts +4 -0
  73. package/dist/trust-mutation.js +29 -0
  74. package/dist/types.d.ts +164 -0
  75. package/dist/types.js +51 -0
  76. package/dist/util/cap-string.d.ts +7 -0
  77. package/dist/util/cap-string.js +35 -0
  78. package/dist/verifier.d.ts +11 -0
  79. package/dist/verifier.js +29 -0
  80. package/dist/whoami.d.ts +7 -0
  81. package/dist/whoami.js +39 -0
  82. package/package.json +68 -0
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.upsertGroupContextParticipants = upsertGroupContextParticipants;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const observability_1 = require("./observability");
6
+ const CURRENT_SCHEMA_VERSION = 1;
7
+ function normalizeDisplayName(externalId, displayName) {
8
+ const trimmed = displayName?.trim();
9
+ return trimmed && trimmed.length > 0 ? trimmed : externalId;
10
+ }
11
+ function buildNameNotes(name, now) {
12
+ return name !== "Unknown"
13
+ ? { name: { value: name, savedAt: now } }
14
+ : {};
15
+ }
16
+ function dedupeParticipants(participants) {
17
+ const deduped = new Map();
18
+ for (const participant of participants) {
19
+ const externalId = participant.externalId.trim();
20
+ if (!externalId)
21
+ continue;
22
+ const key = `${participant.provider}:${externalId}`;
23
+ if (!deduped.has(key)) {
24
+ deduped.set(key, {
25
+ ...participant,
26
+ externalId,
27
+ displayName: participant.displayName?.trim() || undefined,
28
+ });
29
+ }
30
+ }
31
+ return Array.from(deduped.values());
32
+ }
33
+ function createGroupExternalId(provider, groupExternalId, linkedAt) {
34
+ return {
35
+ provider,
36
+ externalId: groupExternalId,
37
+ linkedAt,
38
+ };
39
+ }
40
+ function shouldPromoteToAcquaintance(friend) {
41
+ return (friend.trustLevel ?? "stranger") === "stranger";
42
+ }
43
+ function createAcquaintanceRecord(participant, groupExternalId, linkedAt) {
44
+ const name = normalizeDisplayName(participant.externalId, participant.displayName);
45
+ return {
46
+ id: (0, node_crypto_1.randomUUID)(),
47
+ name,
48
+ role: "acquaintance",
49
+ trustLevel: "acquaintance",
50
+ connections: [],
51
+ externalIds: [
52
+ {
53
+ provider: participant.provider,
54
+ externalId: participant.externalId,
55
+ linkedAt,
56
+ },
57
+ createGroupExternalId(participant.provider, groupExternalId, linkedAt),
58
+ ],
59
+ tenantMemberships: [],
60
+ toolPreferences: {},
61
+ notes: buildNameNotes(name, linkedAt),
62
+ totalTokens: 0,
63
+ createdAt: linkedAt,
64
+ updatedAt: linkedAt,
65
+ schemaVersion: CURRENT_SCHEMA_VERSION,
66
+ };
67
+ }
68
+ async function upsertGroupContextParticipants(input) {
69
+ (0, observability_1.emitNervesEvent)({
70
+ component: "friends",
71
+ event: "friends.group_context_upsert_start",
72
+ message: "upserting shared-group participant context",
73
+ meta: {
74
+ participantCount: input.participants.length,
75
+ hasGroupExternalId: input.groupExternalId.trim().length > 0,
76
+ },
77
+ });
78
+ const groupExternalId = input.groupExternalId.trim();
79
+ if (!groupExternalId) {
80
+ return [];
81
+ }
82
+ const now = input.now ?? (() => new Date().toISOString());
83
+ const participants = dedupeParticipants(input.participants);
84
+ const results = [];
85
+ for (const participant of participants) {
86
+ const linkedAt = now();
87
+ const existing = await input.store.findByExternalId(participant.provider, participant.externalId);
88
+ if (!existing) {
89
+ const created = createAcquaintanceRecord(participant, groupExternalId, linkedAt);
90
+ await input.store.put(created.id, created);
91
+ results.push({
92
+ friendId: created.id,
93
+ name: created.name,
94
+ trustLevel: "acquaintance",
95
+ created: true,
96
+ updated: false,
97
+ addedGroupExternalId: true,
98
+ });
99
+ continue;
100
+ }
101
+ const hasGroupExternalId = existing.externalIds.some((externalId) => externalId.externalId === groupExternalId);
102
+ const promoteToAcquaintance = shouldPromoteToAcquaintance(existing);
103
+ const trustLevel = promoteToAcquaintance
104
+ ? "acquaintance"
105
+ : existing.trustLevel;
106
+ const role = promoteToAcquaintance
107
+ ? "acquaintance"
108
+ : existing.role;
109
+ const updatedExternalIds = hasGroupExternalId
110
+ ? existing.externalIds
111
+ : [...existing.externalIds, createGroupExternalId(participant.provider, groupExternalId, linkedAt)];
112
+ const updated = promoteToAcquaintance || !hasGroupExternalId;
113
+ const record = updated
114
+ ? {
115
+ ...existing,
116
+ role,
117
+ trustLevel,
118
+ externalIds: updatedExternalIds,
119
+ updatedAt: linkedAt,
120
+ }
121
+ : existing;
122
+ if (updated) {
123
+ await input.store.put(record.id, record);
124
+ }
125
+ results.push({
126
+ friendId: record.id,
127
+ name: record.name,
128
+ trustLevel,
129
+ created: false,
130
+ updated,
131
+ addedGroupExternalId: !hasGroupExternalId,
132
+ });
133
+ }
134
+ (0, observability_1.emitNervesEvent)({
135
+ component: "friends",
136
+ event: "friends.group_context_upsert_end",
137
+ message: "upserted shared-group participant context",
138
+ meta: {
139
+ participantCount: participants.length,
140
+ updatedCount: results.filter((result) => result.created || result.updated).length,
141
+ },
142
+ });
143
+ return results;
144
+ }
@@ -0,0 +1,49 @@
1
+ export type { FriendRecord, FriendConnection, ExternalId, IdentityProvider, Integration, Channel, TrustLevel, AgentMeta, AgentAttribution, RelationshipOutcome, NoteProvenance, ImportedNote, ShareScope, ShareGrant, MissionKey, MissionLearning, ImportedLearning, MissionRecord, CoordinationIntent, CoordinationLogEntry, MissionCoordination, ChannelCapabilities, ResolvedContext, SenseType, } from "./types";
2
+ export type { Facing } from "./channel";
3
+ export type { TrustExplanation, TrustBasis } from "./trust-explanation";
4
+ export type { Standing, StandingTier, StandingTally, StandingExplanation, StandingRule, StandingRuleInput, } from "./standing";
5
+ export type { FriendStore } from "./store";
6
+ export type { GrantStore } from "./grant-store";
7
+ export type { MissionStore } from "./mission-store";
8
+ export type { FriendResolverParams } from "./resolver";
9
+ export type { GroupContextParticipant, GroupContextUpsertResult, } from "./group-context";
10
+ export type { UsageData } from "./tokens";
11
+ export type { FriendOpResult, FriendOpStatus } from "./results";
12
+ export type { ApplyFriendNoteInput } from "./notes";
13
+ export type { RoomView, RoomMember, RoomKnownVia } from "./room";
14
+ export type { ConsentPolicy, ConsentRecipient, ConsentDecisionInput, } from "./consent";
15
+ export type { AgentVerifier } from "./verifier";
16
+ export type { ProfileShareEnvelope, SharedNote, PrepareProfileShareInput, PrepareProfileShareResult, PrepareProfileShareStatus, ImportProfileShareInput, ImportProfileShareOptions, ImportProfileShareResult, ImportProfileShareStatus, } from "./share";
17
+ export type { GrantShareInput, RevokeShareResult, ListSharesFilter, ListedShare, } from "./grants";
18
+ export { TRUSTED_LEVELS, IDENTITY_SCOPES, isTrustedLevel, isIdentityProvider, isIntegration, isShareScope, isCoordinationIntent, } from "./types";
19
+ export { FileFriendStore } from "./store-file";
20
+ export { FileGrantStore, grantsDirFor } from "./grant-store-file";
21
+ export { FileMissionStore, missionsDirFor } from "./mission-store-file";
22
+ export { openFileBundle } from "./file-bundle";
23
+ export type { FileBundle } from "./file-bundle";
24
+ export { FriendResolver, machineOwnerUsername, isLocalMachineOwnerIdentity, _setMachineOwnerUsernameForTest, } from "./resolver";
25
+ export { getChannelCapabilities, channelToFacing, isRemoteChannel, getAlwaysOnSenseNames, } from "./channel";
26
+ export { describeTrustContext } from "./trust-explanation";
27
+ export { assessStanding, explainStanding, DEFAULT_STANDING_RULE } from "./standing";
28
+ export { upsertGroupContextParticipants } from "./group-context";
29
+ export { accumulateFriendTokens } from "./tokens";
30
+ export { applyFriendNote } from "./notes";
31
+ export { setFriendTrust } from "./trust-mutation";
32
+ export { linkExternalId, unlinkExternalId } from "./link-identity";
33
+ export { upsertAgentPeer } from "./agent-peer";
34
+ export { recordRelationshipOutcome } from "./outcomes";
35
+ export { recordMission } from "./missions";
36
+ export type { RecordMissionInput } from "./missions";
37
+ export { whoami } from "./whoami";
38
+ export type { WhoamiResult } from "./whoami";
39
+ export { resolveRoom } from "./room";
40
+ export { strictPolicy, trustImpliedPolicy, tieredPolicy, DEFAULT_CONSENT_POLICY, } from "./consent";
41
+ export { tofuVerifier, DEFAULT_AGENT_VERIFIER } from "./verifier";
42
+ export { prepareProfileShare, importProfileShare } from "./share";
43
+ export { prepareMissionShare, importMissionShare } from "./mission-share";
44
+ export type { MissionShareEnvelope, SharedLearning, PrepareMissionShareInput, PrepareMissionShareResult, PrepareMissionShareStatus, ImportMissionShareInput, ImportMissionShareOptions, ImportMissionShareResult, ImportMissionShareStatus, } from "./mission-share";
45
+ export { prepareCoordination, importCoordination } from "./coordination";
46
+ export type { CoordinationEnvelope, PrepareCoordinationInput, PrepareCoordinationResult, PrepareCoordinationStatus, ImportCoordinationInput, ImportCoordinationOptions, ImportCoordinationResult, ImportCoordinationStatus, } from "./coordination";
47
+ export { grantShare, revokeShare, listShares, isGrantEffective } from "./grants";
48
+ export { emitNervesEvent, setNervesEmitter, } from "./observability";
49
+ export type { NervesEvent, NervesEmitter, LogLevel } from "./observability";
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ // @ouro.bot/friends — public API barrel.
3
+ //
4
+ // The who's-who / identity / relationship substrate for agents: a trust ladder
5
+ // (family / friend / acquaintance / stranger), multi-party (group) and
6
+ // multi-agent (a2a peer) aware, consumed through the FriendStore interface +
7
+ // FriendResolver.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.revokeShare = exports.grantShare = exports.importCoordination = exports.prepareCoordination = exports.importMissionShare = exports.prepareMissionShare = exports.importProfileShare = exports.prepareProfileShare = exports.DEFAULT_AGENT_VERIFIER = exports.tofuVerifier = exports.DEFAULT_CONSENT_POLICY = exports.tieredPolicy = exports.trustImpliedPolicy = exports.strictPolicy = exports.resolveRoom = exports.whoami = exports.recordMission = exports.recordRelationshipOutcome = exports.upsertAgentPeer = exports.unlinkExternalId = exports.linkExternalId = exports.setFriendTrust = exports.applyFriendNote = exports.accumulateFriendTokens = exports.upsertGroupContextParticipants = exports.DEFAULT_STANDING_RULE = exports.explainStanding = exports.assessStanding = exports.describeTrustContext = exports.getAlwaysOnSenseNames = exports.isRemoteChannel = exports.channelToFacing = exports.getChannelCapabilities = exports._setMachineOwnerUsernameForTest = exports.isLocalMachineOwnerIdentity = exports.machineOwnerUsername = exports.FriendResolver = exports.openFileBundle = exports.missionsDirFor = exports.FileMissionStore = exports.grantsDirFor = exports.FileGrantStore = exports.FileFriendStore = exports.isCoordinationIntent = exports.isShareScope = exports.isIntegration = exports.isIdentityProvider = exports.isTrustedLevel = exports.IDENTITY_SCOPES = exports.TRUSTED_LEVELS = void 0;
10
+ exports.setNervesEmitter = exports.emitNervesEvent = exports.isGrantEffective = exports.listShares = void 0;
11
+ // -- Values --
12
+ var types_1 = require("./types");
13
+ Object.defineProperty(exports, "TRUSTED_LEVELS", { enumerable: true, get: function () { return types_1.TRUSTED_LEVELS; } });
14
+ Object.defineProperty(exports, "IDENTITY_SCOPES", { enumerable: true, get: function () { return types_1.IDENTITY_SCOPES; } });
15
+ Object.defineProperty(exports, "isTrustedLevel", { enumerable: true, get: function () { return types_1.isTrustedLevel; } });
16
+ Object.defineProperty(exports, "isIdentityProvider", { enumerable: true, get: function () { return types_1.isIdentityProvider; } });
17
+ Object.defineProperty(exports, "isIntegration", { enumerable: true, get: function () { return types_1.isIntegration; } });
18
+ Object.defineProperty(exports, "isShareScope", { enumerable: true, get: function () { return types_1.isShareScope; } });
19
+ Object.defineProperty(exports, "isCoordinationIntent", { enumerable: true, get: function () { return types_1.isCoordinationIntent; } });
20
+ var store_file_1 = require("./store-file");
21
+ Object.defineProperty(exports, "FileFriendStore", { enumerable: true, get: function () { return store_file_1.FileFriendStore; } });
22
+ var grant_store_file_1 = require("./grant-store-file");
23
+ Object.defineProperty(exports, "FileGrantStore", { enumerable: true, get: function () { return grant_store_file_1.FileGrantStore; } });
24
+ Object.defineProperty(exports, "grantsDirFor", { enumerable: true, get: function () { return grant_store_file_1.grantsDirFor; } });
25
+ var mission_store_file_1 = require("./mission-store-file");
26
+ Object.defineProperty(exports, "FileMissionStore", { enumerable: true, get: function () { return mission_store_file_1.FileMissionStore; } });
27
+ Object.defineProperty(exports, "missionsDirFor", { enumerable: true, get: function () { return mission_store_file_1.missionsDirFor; } });
28
+ var file_bundle_1 = require("./file-bundle");
29
+ Object.defineProperty(exports, "openFileBundle", { enumerable: true, get: function () { return file_bundle_1.openFileBundle; } });
30
+ var resolver_1 = require("./resolver");
31
+ Object.defineProperty(exports, "FriendResolver", { enumerable: true, get: function () { return resolver_1.FriendResolver; } });
32
+ Object.defineProperty(exports, "machineOwnerUsername", { enumerable: true, get: function () { return resolver_1.machineOwnerUsername; } });
33
+ Object.defineProperty(exports, "isLocalMachineOwnerIdentity", { enumerable: true, get: function () { return resolver_1.isLocalMachineOwnerIdentity; } });
34
+ Object.defineProperty(exports, "_setMachineOwnerUsernameForTest", { enumerable: true, get: function () { return resolver_1._setMachineOwnerUsernameForTest; } });
35
+ var channel_1 = require("./channel");
36
+ Object.defineProperty(exports, "getChannelCapabilities", { enumerable: true, get: function () { return channel_1.getChannelCapabilities; } });
37
+ Object.defineProperty(exports, "channelToFacing", { enumerable: true, get: function () { return channel_1.channelToFacing; } });
38
+ Object.defineProperty(exports, "isRemoteChannel", { enumerable: true, get: function () { return channel_1.isRemoteChannel; } });
39
+ Object.defineProperty(exports, "getAlwaysOnSenseNames", { enumerable: true, get: function () { return channel_1.getAlwaysOnSenseNames; } });
40
+ var trust_explanation_1 = require("./trust-explanation");
41
+ Object.defineProperty(exports, "describeTrustContext", { enumerable: true, get: function () { return trust_explanation_1.describeTrustContext; } });
42
+ // -- Earned standing (brick four): advisory, first-party, derived; never writes trust --
43
+ var standing_1 = require("./standing");
44
+ Object.defineProperty(exports, "assessStanding", { enumerable: true, get: function () { return standing_1.assessStanding; } });
45
+ Object.defineProperty(exports, "explainStanding", { enumerable: true, get: function () { return standing_1.explainStanding; } });
46
+ Object.defineProperty(exports, "DEFAULT_STANDING_RULE", { enumerable: true, get: function () { return standing_1.DEFAULT_STANDING_RULE; } });
47
+ var group_context_1 = require("./group-context");
48
+ Object.defineProperty(exports, "upsertGroupContextParticipants", { enumerable: true, get: function () { return group_context_1.upsertGroupContextParticipants; } });
49
+ var tokens_1 = require("./tokens");
50
+ Object.defineProperty(exports, "accumulateFriendTokens", { enumerable: true, get: function () { return tokens_1.accumulateFriendTokens; } });
51
+ var notes_1 = require("./notes");
52
+ Object.defineProperty(exports, "applyFriendNote", { enumerable: true, get: function () { return notes_1.applyFriendNote; } });
53
+ var trust_mutation_1 = require("./trust-mutation");
54
+ Object.defineProperty(exports, "setFriendTrust", { enumerable: true, get: function () { return trust_mutation_1.setFriendTrust; } });
55
+ var link_identity_1 = require("./link-identity");
56
+ Object.defineProperty(exports, "linkExternalId", { enumerable: true, get: function () { return link_identity_1.linkExternalId; } });
57
+ Object.defineProperty(exports, "unlinkExternalId", { enumerable: true, get: function () { return link_identity_1.unlinkExternalId; } });
58
+ var agent_peer_1 = require("./agent-peer");
59
+ Object.defineProperty(exports, "upsertAgentPeer", { enumerable: true, get: function () { return agent_peer_1.upsertAgentPeer; } });
60
+ var outcomes_1 = require("./outcomes");
61
+ Object.defineProperty(exports, "recordRelationshipOutcome", { enumerable: true, get: function () { return outcomes_1.recordRelationshipOutcome; } });
62
+ var missions_1 = require("./missions");
63
+ Object.defineProperty(exports, "recordMission", { enumerable: true, get: function () { return missions_1.recordMission; } });
64
+ var whoami_1 = require("./whoami");
65
+ Object.defineProperty(exports, "whoami", { enumerable: true, get: function () { return whoami_1.whoami; } });
66
+ var room_1 = require("./room");
67
+ Object.defineProperty(exports, "resolveRoom", { enumerable: true, get: function () { return room_1.resolveRoom; } });
68
+ // -- Cross-agent moat (N12): consent · share · import --
69
+ // The consent posture is a one-line swap: DEFAULT_CONSENT_POLICY in consent.ts
70
+ // (the SWAP POINT). strictPolicy / trustImpliedPolicy / tieredPolicy are the
71
+ // three selectable postures; tieredPolicy is the default.
72
+ var consent_1 = require("./consent");
73
+ Object.defineProperty(exports, "strictPolicy", { enumerable: true, get: function () { return consent_1.strictPolicy; } });
74
+ Object.defineProperty(exports, "trustImpliedPolicy", { enumerable: true, get: function () { return consent_1.trustImpliedPolicy; } });
75
+ Object.defineProperty(exports, "tieredPolicy", { enumerable: true, get: function () { return consent_1.tieredPolicy; } });
76
+ Object.defineProperty(exports, "DEFAULT_CONSENT_POLICY", { enumerable: true, get: function () { return consent_1.DEFAULT_CONSENT_POLICY; } });
77
+ var verifier_1 = require("./verifier");
78
+ Object.defineProperty(exports, "tofuVerifier", { enumerable: true, get: function () { return verifier_1.tofuVerifier; } });
79
+ Object.defineProperty(exports, "DEFAULT_AGENT_VERIFIER", { enumerable: true, get: function () { return verifier_1.DEFAULT_AGENT_VERIFIER; } });
80
+ var share_1 = require("./share");
81
+ Object.defineProperty(exports, "prepareProfileShare", { enumerable: true, get: function () { return share_1.prepareProfileShare; } });
82
+ Object.defineProperty(exports, "importProfileShare", { enumerable: true, get: function () { return share_1.importProfileShare; } });
83
+ var mission_share_1 = require("./mission-share");
84
+ Object.defineProperty(exports, "prepareMissionShare", { enumerable: true, get: function () { return mission_share_1.prepareMissionShare; } });
85
+ Object.defineProperty(exports, "importMissionShare", { enumerable: true, get: function () { return mission_share_1.importMissionShare; } });
86
+ // -- Coordination / delegation (brick five): negotiate WHO does a mission --
87
+ // Five verbs (request/offer/accept/decline/handoff) over kind:"coordination";
88
+ // the ONLY persisted effect is the mission's `coordination` sub-object (assignee +
89
+ // an append-only log). Trust-gated + consent-gated (the "coordinate" scope),
90
+ // first-party-inviolable, non-transitive (a handoff never forces an assignee).
91
+ var coordination_1 = require("./coordination");
92
+ Object.defineProperty(exports, "prepareCoordination", { enumerable: true, get: function () { return coordination_1.prepareCoordination; } });
93
+ Object.defineProperty(exports, "importCoordination", { enumerable: true, get: function () { return coordination_1.importCoordination; } });
94
+ var grants_1 = require("./grants");
95
+ Object.defineProperty(exports, "grantShare", { enumerable: true, get: function () { return grants_1.grantShare; } });
96
+ Object.defineProperty(exports, "revokeShare", { enumerable: true, get: function () { return grants_1.revokeShare; } });
97
+ Object.defineProperty(exports, "listShares", { enumerable: true, get: function () { return grants_1.listShares; } });
98
+ Object.defineProperty(exports, "isGrantEffective", { enumerable: true, get: function () { return grants_1.isGrantEffective; } });
99
+ // -- Observability seam --
100
+ // The package emits structured events through a no-op `emitNervesEvent` by
101
+ // default. Pass a real emitter via `setNervesEmitter` to forward them (the
102
+ // harness wires its nerves emitter here).
103
+ var observability_1 = require("./observability");
104
+ Object.defineProperty(exports, "emitNervesEvent", { enumerable: true, get: function () { return observability_1.emitNervesEvent; } });
105
+ Object.defineProperty(exports, "setNervesEmitter", { enumerable: true, get: function () { return observability_1.setNervesEmitter; } });
@@ -0,0 +1,14 @@
1
+ import type { FriendStore } from "./store";
2
+ import type { IdentityProvider } from "./types";
3
+ import type { FriendOpResult } from "./results";
4
+ export interface LinkExternalIdInput {
5
+ provider: IdentityProvider;
6
+ externalId: string;
7
+ tenantId?: string;
8
+ }
9
+ export declare function linkExternalId(store: FriendStore, friendId: string, input: LinkExternalIdInput): Promise<FriendOpResult>;
10
+ export interface UnlinkExternalIdInput {
11
+ provider: IdentityProvider;
12
+ externalId: string;
13
+ }
14
+ export declare function unlinkExternalId(store: FriendStore, friendId: string, input: UnlinkExternalIdInput): Promise<FriendOpResult>;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.linkExternalId = linkExternalId;
4
+ exports.unlinkExternalId = unlinkExternalId;
5
+ // linkExternalId / unlinkExternalId — structured-result port of the harness's
6
+ // `friend.link` / `friend.unlink`.
7
+ //
8
+ // Linking is the cross-channel unification mechanic: when another friend record
9
+ // (an "orphan") already holds the external id being linked, the two records are
10
+ // merged into the target — the target's notes win on key collision, the higher
11
+ // trust level is kept, the orphan's other external ids are folded in, and the
12
+ // orphan is deleted. A missing friend is a normal `not_found` result.
13
+ const observability_1 = require("./observability");
14
+ const TRUST_RANK = { family: 4, friend: 3, acquaintance: 2, stranger: 1 };
15
+ /* v8 ignore start -- defensive: ?? fallbacks are unreachable when inputs are valid TrustLevel values @preserve */
16
+ function higherTrust(a, b) {
17
+ const rankA = TRUST_RANK[a ?? "stranger"] ?? 1;
18
+ const rankB = TRUST_RANK[b ?? "stranger"] ?? 1;
19
+ return rankA >= rankB ? (a ?? "stranger") : (b ?? "stranger");
20
+ }
21
+ async function linkExternalId(store, friendId, input) {
22
+ (0, observability_1.emitNervesEvent)({
23
+ component: "friends",
24
+ event: "friends.identity_linked",
25
+ message: "linked external identity",
26
+ meta: { provider: input.provider },
27
+ });
28
+ const current = await store.get(friendId);
29
+ if (!current) {
30
+ return { ok: false, status: "not_found", message: "friend record not found" };
31
+ }
32
+ const alreadyLinked = current.externalIds.some((ext) => ext.provider === input.provider && ext.externalId === input.externalId);
33
+ if (alreadyLinked) {
34
+ return { ok: true, status: "noop", message: "identity already linked", record: current };
35
+ }
36
+ const now = new Date().toISOString();
37
+ const linked = {
38
+ provider: input.provider,
39
+ externalId: input.externalId,
40
+ linkedAt: now,
41
+ ...(input.tenantId !== undefined ? { tenantId: input.tenantId } : {}),
42
+ };
43
+ const newExternalIds = [...current.externalIds, linked];
44
+ // Orphan cleanup: find another friend holding this external id. Matched
45
+ // WITHOUT tenantId (D4) so orphan-merge fires across tenant-unqualified
46
+ // records even when this link carries a tenantId.
47
+ const orphan = await store.findByExternalId(input.provider, input.externalId);
48
+ let mergedNotes = { ...current.notes };
49
+ let mergedTrust = current.trustLevel;
50
+ let orphanExternalIds = [];
51
+ let merged = false;
52
+ if (orphan && orphan.id !== friendId) {
53
+ mergedNotes = { ...orphan.notes, ...current.notes };
54
+ mergedTrust = higherTrust(current.trustLevel, orphan.trustLevel);
55
+ orphanExternalIds = orphan.externalIds.filter((ext) => !(ext.provider === input.provider && ext.externalId === input.externalId));
56
+ await store.delete(orphan.id);
57
+ merged = true;
58
+ }
59
+ const updated = {
60
+ ...current,
61
+ externalIds: [...newExternalIds, ...orphanExternalIds],
62
+ notes: mergedNotes,
63
+ trustLevel: mergedTrust,
64
+ updatedAt: now,
65
+ };
66
+ await store.put(friendId, updated);
67
+ return { ok: true, status: merged ? "merged" : "linked", record: updated };
68
+ }
69
+ async function unlinkExternalId(store, friendId, input) {
70
+ (0, observability_1.emitNervesEvent)({
71
+ component: "friends",
72
+ event: "friends.identity_unlinked",
73
+ message: "unlinked external identity",
74
+ meta: { provider: input.provider },
75
+ });
76
+ const current = await store.get(friendId);
77
+ if (!current) {
78
+ return { ok: false, status: "not_found", message: "friend record not found" };
79
+ }
80
+ const idx = current.externalIds.findIndex((ext) => ext.provider === input.provider && ext.externalId === input.externalId);
81
+ if (idx === -1) {
82
+ return { ok: false, status: "noop", message: "identity not linked" };
83
+ }
84
+ const filtered = current.externalIds.filter((_, i) => i !== idx);
85
+ const updated = { ...current, externalIds: filtered, updatedAt: new Date().toISOString() };
86
+ await store.put(friendId, updated);
87
+ return { ok: true, status: "unlinked", record: updated };
88
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ // friends-mcp — stdio entrypoint for the friends MCP server.
5
+ //
6
+ // Thin wrapper: all arg-parsing and store construction live in `runMain` (which
7
+ // is covered by tests). This file is the only module excluded from coverage —
8
+ // its sole uncovered lines are the process wiring below.
9
+ const run_main_1 = require("./run-main");
10
+ (0, run_main_1.runMain)(process.argv, process.env, {
11
+ stdin: process.stdin,
12
+ stdout: process.stdout,
13
+ onError: () => {
14
+ process.exitCode = 1;
15
+ },
16
+ });
@@ -0,0 +1,14 @@
1
+ import type { FriendStore } from "../store";
2
+ import type { GrantStore } from "../grant-store";
3
+ import type { MissionStore } from "../mission-store";
4
+ type Args = Record<string, unknown>;
5
+ export interface DispatchResult {
6
+ result: unknown;
7
+ isError: boolean;
8
+ }
9
+ export declare function coerceBool(v: unknown): boolean;
10
+ export declare function coerceInt(v: unknown): number | undefined;
11
+ export declare function coerceString(v: unknown): string;
12
+ export declare function coerceOptionalString(v: unknown): string | undefined;
13
+ export declare function dispatchTool(store: FriendStore, name: string, args: Args, grants?: GrantStore, missions?: MissionStore): Promise<DispatchResult>;
14
+ export {};