@indexnetwork/protocol 14.3.2-rc.477.1 → 16.1.1-rc.478.1
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/CHANGELOG.md +107 -0
- package/IMPLEMENTATION.md +50 -52
- package/STABILITY.md +3 -3
- package/dist/chat/chat.agent.js +2 -2
- package/dist/chat/chat.graph.d.ts +41 -1
- package/dist/chat/chat.graph.js +108 -127
- package/dist/discovery/hyde.frame.d.ts +4 -4
- package/dist/discovery/hyde.graph.d.ts +71 -12
- package/dist/discovery/hyde.graph.js +337 -335
- package/dist/discovery/lens.inferrer.d.ts +6 -6
- package/dist/enrichment/enrichment.graph.d.ts +127 -9
- package/dist/enrichment/enrichment.graph.js +633 -655
- package/dist/enrichment/enrichment.state.d.ts +1 -1
- package/dist/enrichment/enrichment.tools.context-read.d.ts +10 -0
- package/dist/enrichment/enrichment.tools.context-read.js +353 -0
- package/dist/enrichment/enrichment.tools.context-write.d.ts +9 -0
- package/dist/enrichment/enrichment.tools.context-write.js +417 -0
- package/dist/enrichment/enrichment.tools.d.ts +11 -1
- package/dist/enrichment/enrichment.tools.helpers.d.ts +139 -0
- package/dist/enrichment/enrichment.tools.helpers.js +234 -0
- package/dist/enrichment/enrichment.tools.js +16 -975
- package/dist/index.d.ts +27 -48
- package/dist/index.js +17 -27
- package/dist/intents/application/intent.graph.d.ts +71 -67
- package/dist/intents/application/intent.graph.execute.d.ts +59 -0
- package/dist/intents/application/intent.graph.execute.js +301 -0
- package/dist/intents/application/intent.graph.infer.d.ts +44 -0
- package/dist/intents/application/intent.graph.infer.js +97 -0
- package/dist/intents/application/intent.graph.js +96 -888
- package/dist/intents/application/intent.graph.reconcile.d.ts +71 -0
- package/dist/intents/application/intent.graph.reconcile.js +274 -0
- package/dist/intents/application/intent.graph.shared.d.ts +70 -0
- package/dist/intents/application/intent.graph.shared.js +153 -0
- package/dist/intents/domain/intent.state.d.ts +1 -1
- package/dist/maintenance/maintenance.graph.d.ts +66 -3
- package/dist/maintenance/maintenance.graph.js +155 -156
- package/dist/mcp/mcp.authorization-policy.d.ts +24 -28
- package/dist/mcp/mcp.authorization-policy.js +14 -34
- package/dist/mcp/mcp.server.d.ts +3 -3
- package/dist/mcp/mcp.server.js +12 -16
- package/dist/negotiations/application/negotiation.candidates.d.ts +83 -0
- package/dist/negotiations/application/negotiation.candidates.js +162 -0
- package/dist/negotiations/application/negotiation.graph.d.ts +91 -157
- package/dist/negotiations/application/negotiation.graph.finalize.d.ts +5 -0
- package/dist/negotiations/application/negotiation.graph.finalize.js +280 -0
- package/dist/negotiations/application/negotiation.graph.init.d.ts +54 -0
- package/dist/negotiations/application/negotiation.graph.init.js +227 -0
- package/dist/negotiations/application/negotiation.graph.js +69 -1388
- package/dist/negotiations/application/negotiation.graph.screen.d.ts +23 -0
- package/dist/negotiations/application/negotiation.graph.screen.js +108 -0
- package/dist/negotiations/application/negotiation.graph.shared.d.ts +75 -0
- package/dist/negotiations/application/negotiation.graph.shared.js +125 -0
- package/dist/negotiations/application/negotiation.graph.turn.d.ts +105 -0
- package/dist/negotiations/application/negotiation.graph.turn.js +484 -0
- package/dist/negotiations/domain/negotiation.state.d.ts +1 -1
- package/dist/negotiations/domain/negotiation.state.js +0 -1
- package/dist/networks/application/indexer.graph.d.ts +165 -7
- package/dist/networks/application/indexer.graph.js +339 -388
- package/dist/networks/application/indexer.state.d.ts +1 -1
- package/dist/networks/application/membership.graph.d.ts +83 -5
- package/dist/networks/application/membership.graph.js +177 -207
- package/dist/networks/application/network.graph.d.ts +148 -5
- package/dist/networks/application/network.graph.js +249 -278
- package/dist/networks/domain/membership.state.d.ts +1 -1
- package/dist/networks/domain/network.state.d.ts +1 -1
- package/dist/opportunities/application/delivery-card.cache.d.ts +1 -1
- package/dist/opportunities/application/delivery-card.cache.js +2 -2
- package/dist/opportunities/application/index.d.ts +2 -2
- package/dist/opportunities/application/index.js +2 -2
- package/dist/opportunities/application/opportunity.evaluator.js +1 -1
- package/dist/opportunities/application/opportunity.graph.d.ts +686 -473
- package/dist/opportunities/application/opportunity.graph.discovery-strategies.d.ts +109 -0
- package/dist/opportunities/application/opportunity.graph.discovery-strategies.js +451 -0
- package/dist/opportunities/application/opportunity.graph.discovery.d.ts +69 -0
- package/dist/opportunities/application/opportunity.graph.discovery.js +397 -0
- package/dist/opportunities/application/opportunity.graph.evaluation.d.ts +82 -0
- package/dist/opportunities/application/opportunity.graph.evaluation.js +608 -0
- package/dist/opportunities/application/opportunity.graph.js +120 -3690
- package/dist/opportunities/application/opportunity.graph.modes.d.ts +538 -0
- package/dist/opportunities/application/opportunity.graph.modes.js +538 -0
- package/dist/opportunities/application/opportunity.graph.negotiate.d.ts +109 -0
- package/dist/opportunities/application/opportunity.graph.negotiate.js +392 -0
- package/dist/opportunities/application/opportunity.graph.persist-node.d.ts +93 -0
- package/dist/opportunities/application/opportunity.graph.persist-node.js +765 -0
- package/dist/opportunities/application/opportunity.graph.prep.d.ts +150 -0
- package/dist/opportunities/application/opportunity.graph.prep.js +381 -0
- package/dist/opportunities/application/opportunity.graph.shared.d.ts +173 -0
- package/dist/opportunities/application/opportunity.graph.shared.js +199 -0
- package/dist/opportunities/application/opportunity.presentation.d.ts +399 -0
- package/dist/opportunities/application/{opportunity.presenter.js → opportunity.presentation.js} +735 -9
- package/dist/opportunities/application/opportunity.tools.cards.d.ts +152 -0
- package/dist/opportunities/application/opportunity.tools.cards.js +235 -0
- package/dist/opportunities/application/opportunity.tools.d.ts +8 -114
- package/dist/opportunities/application/opportunity.tools.js +28 -711
- package/dist/opportunities/application/opportunity.tools.list.d.ts +10 -0
- package/dist/opportunities/application/opportunity.tools.list.js +493 -0
- package/dist/opportunities/domain/index.d.ts +3 -3
- package/dist/opportunities/domain/index.js +3 -3
- package/dist/opportunities/domain/opportunity.state.d.ts +19 -19
- package/dist/opportunities/index.d.ts +6 -6
- package/dist/opportunities/index.js +4 -4
- package/dist/opportunities/ports/opportunity.tools.port.d.ts +1 -1
- package/dist/opportunities/radar/radar.graph.d.ts +57 -13
- package/dist/opportunities/radar/radar.graph.js +470 -471
- package/dist/premises/premise.graph.d.ts +88 -20
- package/dist/premises/premise.graph.js +207 -218
- package/dist/questions/domain/question.schema.d.ts +40 -40
- package/dist/shared/agent/tool.helpers.d.ts +21 -0
- package/dist/shared/agent/tool.registry.d.ts +4 -3
- package/dist/shared/agent/tool.registry.js +1 -1
- package/dist/shared/interfaces/database.capabilities.d.ts +100 -0
- package/dist/shared/interfaces/database.capabilities.js +7 -0
- package/dist/shared/interfaces/database.entities.d.ts +533 -0
- package/dist/shared/interfaces/database.entities.js +10 -0
- package/dist/shared/interfaces/database.identity-queries.d.ts +294 -0
- package/dist/shared/interfaces/database.identity-queries.js +4 -0
- package/dist/shared/interfaces/database.interface.d.ts +15 -2277
- package/dist/shared/interfaces/database.interface.js +8 -0
- package/dist/shared/interfaces/database.member-queries.d.ts +228 -0
- package/dist/shared/interfaces/database.member-queries.js +4 -0
- package/dist/shared/interfaces/database.negotiation.d.ts +305 -0
- package/dist/shared/interfaces/database.negotiation.js +26 -0
- package/dist/shared/interfaces/database.network-queries.d.ts +277 -0
- package/dist/shared/interfaces/database.network-queries.js +4 -0
- package/dist/shared/interfaces/database.opportunity-queries.d.ts +285 -0
- package/dist/shared/interfaces/database.opportunity-queries.js +4 -0
- package/dist/shared/interfaces/database.port.d.ts +322 -0
- package/dist/shared/interfaces/database.port.js +29 -0
- package/dist/shared/schemas/discovery-question.schema.d.ts +14 -14
- package/dist/shared/schemas/negotiation-digest.schema.d.ts +2 -2
- package/package.json +2 -15
- package/dist/opportunities/application/opportunity.card-presentation.d.ts +0 -44
- package/dist/opportunities/application/opportunity.card-presentation.js +0 -93
- package/dist/opportunities/application/opportunity.presenter.d.ts +0 -156
- package/dist/opportunities/domain/opportunity.presentation-cache.d.ts +0 -5
- package/dist/opportunities/domain/opportunity.presentation-cache.js +0 -11
- package/dist/opportunities/domain/opportunity.presentation.d.ts +0 -76
- package/dist/opportunities/domain/opportunity.presentation.js +0 -516
- package/dist/opportunities/domain/opportunity.safe-presentation.d.ts +0 -104
- package/dist/opportunities/domain/opportunity.safe-presentation.js +0 -102
|
@@ -3,301 +3,272 @@ import { protocolLogger } from "../../shared/observability/protocol.logger.js";
|
|
|
3
3
|
import { timed } from "../../shared/observability/performance.js";
|
|
4
4
|
import { NetworkGraphState } from "../domain/index.js";
|
|
5
5
|
const logger = protocolLogger("NetworkGraphFactory");
|
|
6
|
-
/**
|
|
7
|
-
* Factory class to build and compile the Network (community) lifecycle graph.
|
|
8
|
-
*
|
|
9
|
-
* Handles create, read, update, and delete operations for networks.
|
|
10
|
-
* Membership and intent–network assignment operations are delegated to
|
|
11
|
-
* NetworkMembershipGraphFactory and IntentNetworkGraphFactory respectively.
|
|
12
|
-
*
|
|
13
|
-
* ## Ownership policy
|
|
14
|
-
*
|
|
15
|
-
* - `create`: the calling user becomes the sole owner. The owner–membership
|
|
16
|
-
* write is performed atomically after the network row is created; on failure
|
|
17
|
-
* the network is soft-deleted (rollback). Rollback failures are logged but
|
|
18
|
-
* do NOT mutate the error response — the caller always receives the first
|
|
19
|
-
* meaningful failure, not a noisy rollback message.
|
|
20
|
-
* - `update`: owner-only. Any member field may be updated in one call.
|
|
21
|
-
* - `delete`: owner-only; the network must have no other members.
|
|
22
|
-
*
|
|
23
|
-
* ## Scope semantics (read mode)
|
|
24
|
-
*
|
|
25
|
-
* When `networkId` is set and `showAll` is false, only the focused network and
|
|
26
|
-
* the user's personal network (contacts) are returned. This is the strict-scope
|
|
27
|
-
* invariant for network-bound agents. Set `showAll: true` to bypass.
|
|
28
|
-
*
|
|
29
|
-
* Flow:
|
|
30
|
-
* START → routerNode → {createNode | readNode | updateNode | deleteNode} → END
|
|
31
|
-
*
|
|
32
|
-
* IND-546: canonical home — previously network/network.graph.ts.
|
|
33
|
-
*/
|
|
34
6
|
export class NetworkGraphFactory {
|
|
35
7
|
constructor(database) {
|
|
36
|
-
this.
|
|
8
|
+
this.deps = { database };
|
|
37
9
|
}
|
|
38
10
|
createGraph() {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
];
|
|
100
|
-
const owns = [
|
|
101
|
-
...(scopedOwned ? [projectOwned(scopedOwned)] : []),
|
|
102
|
-
...(personalOwned ? [projectOwned(personalOwned)] : []),
|
|
103
|
-
];
|
|
104
|
-
return {
|
|
105
|
-
readResult: {
|
|
106
|
-
memberOf,
|
|
107
|
-
owns,
|
|
108
|
-
stats: {
|
|
109
|
-
memberOfCount: memberOf.length,
|
|
110
|
-
ownsCount: owns.length,
|
|
111
|
-
scopeNote: "Showing current network and your personal network. Use showAll: true for all networks.",
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
// Include public networks available to join
|
|
117
|
-
const publicNetworks = publicIndexesResult.networks.map((idx) => ({
|
|
118
|
-
networkId: idx.id,
|
|
119
|
-
title: idx.title,
|
|
120
|
-
prompt: idx.prompt,
|
|
121
|
-
memberCount: idx.memberCount,
|
|
122
|
-
owner: idx.owner,
|
|
123
|
-
}));
|
|
11
|
+
const deps = this.deps;
|
|
12
|
+
// --- GRAPH ASSEMBLY ---
|
|
13
|
+
const workflow = new StateGraph(NetworkGraphState)
|
|
14
|
+
.addNode("read", (state) => readNode(state, deps))
|
|
15
|
+
.addNode("create", (state) => createNode(state, deps))
|
|
16
|
+
.addNode("update", (state) => updateNode(state, deps))
|
|
17
|
+
.addNode("delete_idx", (state) => deleteNode(state, deps))
|
|
18
|
+
.addConditionalEdges(START, routeByMode, {
|
|
19
|
+
read: "read",
|
|
20
|
+
create: "create",
|
|
21
|
+
update: "update",
|
|
22
|
+
delete_idx: "delete_idx",
|
|
23
|
+
})
|
|
24
|
+
.addEdge("read", END)
|
|
25
|
+
.addEdge("create", END)
|
|
26
|
+
.addEdge("update", END)
|
|
27
|
+
.addEdge("delete_idx", END);
|
|
28
|
+
return workflow.compile();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read Node: List networks the user belongs to and owns.
|
|
33
|
+
*/
|
|
34
|
+
export async function readNode(state, deps) {
|
|
35
|
+
return timed("NetworkGraph.read", async () => {
|
|
36
|
+
logger.verbose("Read networks", { userId: state.userId, networkId: state.networkId, showAll: state.showAll });
|
|
37
|
+
// Shared projections for both the scoped and unscoped read paths.
|
|
38
|
+
const projectMembership = (m) => ({
|
|
39
|
+
networkId: m.networkId,
|
|
40
|
+
title: m.networkTitle,
|
|
41
|
+
prompt: m.indexPrompt,
|
|
42
|
+
autoAssign: m.autoAssign,
|
|
43
|
+
isPersonal: m.isPersonal,
|
|
44
|
+
joinedAt: m.joinedAt,
|
|
45
|
+
});
|
|
46
|
+
const projectOwned = (o) => ({
|
|
47
|
+
networkId: o.id,
|
|
48
|
+
title: o.title,
|
|
49
|
+
prompt: o.prompt,
|
|
50
|
+
memberCount: o.memberCount,
|
|
51
|
+
intentCount: o.intentCount,
|
|
52
|
+
joinPolicy: o.permissions.joinPolicy,
|
|
53
|
+
});
|
|
54
|
+
try {
|
|
55
|
+
const [allMemberships, ownedIndexes, publicIndexesResult] = await Promise.all([
|
|
56
|
+
deps.database.getNetworkMemberships(state.userId),
|
|
57
|
+
deps.database.getOwnedIndexes(state.userId),
|
|
58
|
+
deps.database.getPublicIndexesNotJoined(state.userId),
|
|
59
|
+
]);
|
|
60
|
+
// If network-scoped and not showAll, return just that network plus the
|
|
61
|
+
// user's personal network (their contacts). The personal network is part
|
|
62
|
+
// of every allowed-network reach calculation for network-bound agents,
|
|
63
|
+
// and a user in a community-scoped chat still owns their contact list —
|
|
64
|
+
// so surfacing it here keeps contact tools discoverable. Other community
|
|
65
|
+
// memberships are still hidden, and `publicNetworks` is omitted.
|
|
66
|
+
const scopeToCurrentNetwork = state.networkId && !state.showAll;
|
|
67
|
+
if (scopeToCurrentNetwork) {
|
|
68
|
+
const networkId = state.networkId;
|
|
69
|
+
const isMember = await deps.database.isNetworkMember(networkId, state.userId);
|
|
70
|
+
if (!isMember) {
|
|
124
71
|
return {
|
|
125
72
|
readResult: {
|
|
126
|
-
memberOf:
|
|
127
|
-
owns:
|
|
128
|
-
|
|
129
|
-
stats: { memberOfCount: allMemberships.length, ownsCount: ownedIndexes.length, publicNetworksCount: publicNetworks.length },
|
|
73
|
+
memberOf: [],
|
|
74
|
+
owns: [],
|
|
75
|
+
stats: { memberOfCount: 0, ownsCount: 0, scopeNote: "Network not found or you are not a member." },
|
|
130
76
|
},
|
|
131
77
|
};
|
|
132
78
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const network = await this.database.createNetwork({
|
|
158
|
-
title: state.createInput.title.trim(),
|
|
159
|
-
prompt: state.createInput.prompt?.trim() || undefined,
|
|
160
|
-
imageUrl: state.createInput.imageUrl ?? undefined,
|
|
161
|
-
joinPolicy: state.createInput.joinPolicy,
|
|
162
|
-
});
|
|
163
|
-
createdNetworkId = network.id;
|
|
164
|
-
const added = await this.database.addMemberToNetwork(network.id, state.userId, 'owner');
|
|
165
|
-
if (!added.success) {
|
|
166
|
-
logger.error("addMemberToNetwork failed; cleaning up orphaned network", { networkId: network.id });
|
|
167
|
-
try {
|
|
168
|
-
await this.database.softDeleteNetwork(network.id);
|
|
169
|
-
}
|
|
170
|
-
catch (rollbackError) {
|
|
171
|
-
logger.error("Network create rollback failed", {
|
|
172
|
-
networkId: network.id,
|
|
173
|
-
rollbackFor: "owner_membership",
|
|
174
|
-
rollbackErrorKind: rollbackError instanceof Error ? "error" : "non_error",
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
return { mutationResult: { success: false, error: "Failed to set you as owner. Network was not created." } };
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
mutationResult: {
|
|
181
|
-
success: true,
|
|
182
|
-
networkId: network.id,
|
|
183
|
-
title: network.title,
|
|
184
|
-
message: `Network "${network.title}" created. You are the owner.`,
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
catch (err) {
|
|
189
|
-
logger.error("Create network failed", { error: err });
|
|
190
|
-
if (createdNetworkId) {
|
|
191
|
-
try {
|
|
192
|
-
await this.database.softDeleteNetwork(createdNetworkId);
|
|
193
|
-
}
|
|
194
|
-
catch (rollbackError) {
|
|
195
|
-
logger.error("Network create rollback failed", {
|
|
196
|
-
networkId: createdNetworkId,
|
|
197
|
-
rollbackFor: "create_operation",
|
|
198
|
-
rollbackErrorKind: rollbackError instanceof Error ? "error" : "non_error",
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
mutationResult: {
|
|
204
|
-
success: false,
|
|
205
|
-
error: err instanceof Error ? err.message : "Failed to create network.",
|
|
79
|
+
const scopedMembership = allMemberships.find((m) => m.networkId === networkId);
|
|
80
|
+
const personalMembership = scopedMembership?.isPersonal
|
|
81
|
+
? undefined
|
|
82
|
+
: allMemberships.find((m) => m.isPersonal);
|
|
83
|
+
const scopedOwned = ownedIndexes.find((o) => o.id === networkId);
|
|
84
|
+
const personalOwned = personalMembership
|
|
85
|
+
? ownedIndexes.find((o) => o.id === personalMembership.networkId)
|
|
86
|
+
: undefined;
|
|
87
|
+
const memberOf = [
|
|
88
|
+
...(scopedMembership ? [projectMembership(scopedMembership)] : []),
|
|
89
|
+
...(personalMembership ? [projectMembership(personalMembership)] : []),
|
|
90
|
+
];
|
|
91
|
+
const owns = [
|
|
92
|
+
...(scopedOwned ? [projectOwned(scopedOwned)] : []),
|
|
93
|
+
...(personalOwned ? [projectOwned(personalOwned)] : []),
|
|
94
|
+
];
|
|
95
|
+
return {
|
|
96
|
+
readResult: {
|
|
97
|
+
memberOf,
|
|
98
|
+
owns,
|
|
99
|
+
stats: {
|
|
100
|
+
memberOfCount: memberOf.length,
|
|
101
|
+
ownsCount: owns.length,
|
|
102
|
+
scopeNote: "Showing current network and your personal network. Use showAll: true for all networks.",
|
|
206
103
|
},
|
|
207
|
-
}
|
|
208
|
-
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Include public networks available to join
|
|
108
|
+
const publicNetworks = publicIndexesResult.networks.map((idx) => ({
|
|
109
|
+
networkId: idx.id,
|
|
110
|
+
title: idx.title,
|
|
111
|
+
prompt: idx.prompt,
|
|
112
|
+
memberCount: idx.memberCount,
|
|
113
|
+
owner: idx.owner,
|
|
114
|
+
}));
|
|
115
|
+
return {
|
|
116
|
+
readResult: {
|
|
117
|
+
memberOf: allMemberships.map(projectMembership),
|
|
118
|
+
owns: ownedIndexes.map(projectOwned),
|
|
119
|
+
publicNetworks,
|
|
120
|
+
stats: { memberOfCount: allMemberships.length, ownsCount: ownedIndexes.length, publicNetworksCount: publicNetworks.length },
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
logger.error("Read networks failed", { error: err });
|
|
126
|
+
return { error: "Failed to fetch network information." };
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create Node: Create a new network and add user as owner.
|
|
132
|
+
*
|
|
133
|
+
* Atomicity contract:
|
|
134
|
+
* 1. Create the network row.
|
|
135
|
+
* 2. Add the calling user as owner.
|
|
136
|
+
* 3. On step-2 failure: soft-delete the network (rollback).
|
|
137
|
+
* 4. Rollback failures are logged at error level but never override the
|
|
138
|
+
* primary failure message returned to the caller.
|
|
139
|
+
*/
|
|
140
|
+
export async function createNode(state, deps) {
|
|
141
|
+
return timed("NetworkGraph.create", async () => {
|
|
142
|
+
logger.verbose("Create network", { userId: state.userId, createInput: state.createInput });
|
|
143
|
+
if (!state.createInput?.title?.trim()) {
|
|
144
|
+
return { mutationResult: { success: false, error: "Title is required." } };
|
|
145
|
+
}
|
|
146
|
+
let createdNetworkId;
|
|
147
|
+
try {
|
|
148
|
+
const network = await deps.database.createNetwork({
|
|
149
|
+
title: state.createInput.title.trim(),
|
|
150
|
+
prompt: state.createInput.prompt?.trim() || undefined,
|
|
151
|
+
imageUrl: state.createInput.imageUrl ?? undefined,
|
|
152
|
+
joinPolicy: state.createInput.joinPolicy,
|
|
209
153
|
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const updateNode = async (state) => {
|
|
215
|
-
return timed("NetworkGraph.update", async () => {
|
|
216
|
-
const networkId = state.networkId;
|
|
217
|
-
logger.verbose("Update network", { userId: state.userId, networkId, updateInput: state.updateInput });
|
|
218
|
-
if (!networkId) {
|
|
219
|
-
return { mutationResult: { success: false, error: "networkId is required for update." } };
|
|
220
|
-
}
|
|
154
|
+
createdNetworkId = network.id;
|
|
155
|
+
const added = await deps.database.addMemberToNetwork(network.id, state.userId, 'owner');
|
|
156
|
+
if (!added.success) {
|
|
157
|
+
logger.error("addMemberToNetwork failed; cleaning up orphaned network", { networkId: network.id });
|
|
221
158
|
try {
|
|
222
|
-
|
|
223
|
-
if (!isOwner) {
|
|
224
|
-
return { mutationResult: { success: false, error: "You can only modify networks you own." } };
|
|
225
|
-
}
|
|
226
|
-
await this.database.updateIndexSettings(networkId, state.userId, state.updateInput ?? {});
|
|
227
|
-
return {
|
|
228
|
-
mutationResult: {
|
|
229
|
-
success: true,
|
|
230
|
-
networkId,
|
|
231
|
-
message: "Network settings updated.",
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
catch (err) {
|
|
236
|
-
logger.error("Update network failed", { error: err });
|
|
237
|
-
return { mutationResult: { success: false, error: "Failed to update network." } };
|
|
159
|
+
await deps.database.softDeleteNetwork(network.id);
|
|
238
160
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return timed("NetworkGraph.delete", async () => {
|
|
246
|
-
const networkId = state.networkId;
|
|
247
|
-
logger.verbose("Delete network", { userId: state.userId, networkId });
|
|
248
|
-
if (!networkId) {
|
|
249
|
-
return { mutationResult: { success: false, error: "networkId is required for delete." } };
|
|
161
|
+
catch (rollbackError) {
|
|
162
|
+
logger.error("Network create rollback failed", {
|
|
163
|
+
networkId: network.id,
|
|
164
|
+
rollbackFor: "owner_membership",
|
|
165
|
+
rollbackErrorKind: rollbackError instanceof Error ? "error" : "non_error",
|
|
166
|
+
});
|
|
250
167
|
}
|
|
168
|
+
return { mutationResult: { success: false, error: "Failed to set you as owner. Network was not created." } };
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
mutationResult: {
|
|
172
|
+
success: true,
|
|
173
|
+
networkId: network.id,
|
|
174
|
+
title: network.title,
|
|
175
|
+
message: `Network "${network.title}" created. You are the owner.`,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
logger.error("Create network failed", { error: err });
|
|
181
|
+
if (createdNetworkId) {
|
|
251
182
|
try {
|
|
252
|
-
|
|
253
|
-
if (!isOwner) {
|
|
254
|
-
return { mutationResult: { success: false, error: "You can only delete networks you own." } };
|
|
255
|
-
}
|
|
256
|
-
const count = await this.database.getNetworkMemberCount(networkId);
|
|
257
|
-
if (count > 1) {
|
|
258
|
-
return { mutationResult: { success: false, error: "Cannot delete network with other members. Remove members first." } };
|
|
259
|
-
}
|
|
260
|
-
await this.database.softDeleteNetwork(networkId);
|
|
261
|
-
return {
|
|
262
|
-
mutationResult: {
|
|
263
|
-
success: true,
|
|
264
|
-
networkId,
|
|
265
|
-
message: "Network deleted.",
|
|
266
|
-
},
|
|
267
|
-
};
|
|
183
|
+
await deps.database.softDeleteNetwork(createdNetworkId);
|
|
268
184
|
}
|
|
269
|
-
catch (
|
|
270
|
-
logger.error("
|
|
271
|
-
|
|
185
|
+
catch (rollbackError) {
|
|
186
|
+
logger.error("Network create rollback failed", {
|
|
187
|
+
networkId: createdNetworkId,
|
|
188
|
+
rollbackFor: "create_operation",
|
|
189
|
+
rollbackErrorKind: rollbackError instanceof Error ? "error" : "non_error",
|
|
190
|
+
});
|
|
272
191
|
}
|
|
273
|
-
});
|
|
274
|
-
};
|
|
275
|
-
// --- CONDITIONAL ROUTING ---
|
|
276
|
-
const routeByMode = (state) => {
|
|
277
|
-
switch (state.operationMode) {
|
|
278
|
-
case 'create': return 'create';
|
|
279
|
-
case 'read': return 'read';
|
|
280
|
-
case 'update': return 'update';
|
|
281
|
-
case 'delete': return 'delete_idx';
|
|
282
|
-
default: return 'read';
|
|
283
192
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
193
|
+
return {
|
|
194
|
+
mutationResult: {
|
|
195
|
+
success: false,
|
|
196
|
+
error: err instanceof Error ? err.message : "Failed to create network.",
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Update Node: Update network settings (owner only).
|
|
204
|
+
*/
|
|
205
|
+
export async function updateNode(state, deps) {
|
|
206
|
+
return timed("NetworkGraph.update", async () => {
|
|
207
|
+
const networkId = state.networkId;
|
|
208
|
+
logger.verbose("Update network", { userId: state.userId, networkId, updateInput: state.updateInput });
|
|
209
|
+
if (!networkId) {
|
|
210
|
+
return { mutationResult: { success: false, error: "networkId is required for update." } };
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const isOwner = await deps.database.isIndexOwner(networkId, state.userId);
|
|
214
|
+
if (!isOwner) {
|
|
215
|
+
return { mutationResult: { success: false, error: "You can only modify networks you own." } };
|
|
216
|
+
}
|
|
217
|
+
await deps.database.updateIndexSettings(networkId, state.userId, state.updateInput ?? {});
|
|
218
|
+
return {
|
|
219
|
+
mutationResult: {
|
|
220
|
+
success: true,
|
|
221
|
+
networkId,
|
|
222
|
+
message: "Network settings updated.",
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
logger.error("Update network failed", { error: err });
|
|
228
|
+
return { mutationResult: { success: false, error: "Failed to update network." } };
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Delete Node: Soft-delete a network (owner only, sole member).
|
|
234
|
+
*/
|
|
235
|
+
export async function deleteNode(state, deps) {
|
|
236
|
+
return timed("NetworkGraph.delete", async () => {
|
|
237
|
+
const networkId = state.networkId;
|
|
238
|
+
logger.verbose("Delete network", { userId: state.userId, networkId });
|
|
239
|
+
if (!networkId) {
|
|
240
|
+
return { mutationResult: { success: false, error: "networkId is required for delete." } };
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const isOwner = await deps.database.isIndexOwner(networkId, state.userId);
|
|
244
|
+
if (!isOwner) {
|
|
245
|
+
return { mutationResult: { success: false, error: "You can only delete networks you own." } };
|
|
246
|
+
}
|
|
247
|
+
const count = await deps.database.getNetworkMemberCount(networkId);
|
|
248
|
+
if (count > 1) {
|
|
249
|
+
return { mutationResult: { success: false, error: "Cannot delete network with other members. Remove members first." } };
|
|
250
|
+
}
|
|
251
|
+
await deps.database.softDeleteNetwork(networkId);
|
|
252
|
+
return {
|
|
253
|
+
mutationResult: {
|
|
254
|
+
success: true,
|
|
255
|
+
networkId,
|
|
256
|
+
message: "Network deleted.",
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
logger.error("Delete network failed", { error: err });
|
|
262
|
+
return { mutationResult: { success: false, error: "Failed to delete network." } };
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
export function routeByMode(state) {
|
|
267
|
+
switch (state.operationMode) {
|
|
268
|
+
case 'create': return 'create';
|
|
269
|
+
case 'read': return 'read';
|
|
270
|
+
case 'update': return 'update';
|
|
271
|
+
case 'delete': return 'delete_idx';
|
|
272
|
+
default: return 'read';
|
|
302
273
|
}
|
|
303
274
|
}
|
|
@@ -30,7 +30,7 @@ export declare const NetworkMembershipGraphState: import("@langchain/langgraph")
|
|
|
30
30
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
31
31
|
};
|
|
32
32
|
/** Operation mode. */
|
|
33
|
-
operationMode: import("@langchain/langgraph").BaseChannel<"
|
|
33
|
+
operationMode: import("@langchain/langgraph").BaseChannel<"create" | "delete" | "read", "create" | "delete" | "read" | import("@langchain/langgraph").OverwriteValue<"create" | "delete" | "read">, unknown>;
|
|
34
34
|
/** For create/delete: the user being added/removed. */
|
|
35
35
|
targetUserId: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
|
|
36
36
|
/** Output for read mode: list of members. */
|
|
@@ -23,7 +23,7 @@ export declare const NetworkGraphState: import("@langchain/langgraph").Annotatio
|
|
|
23
23
|
/** Target network ID. Required for read/update/delete. From ChatGraph or tool arg. */
|
|
24
24
|
networkId: import("@langchain/langgraph").BaseChannel<string | undefined, string | import("@langchain/langgraph").OverwriteValue<string | undefined> | undefined, unknown>;
|
|
25
25
|
/** Operation mode. */
|
|
26
|
-
operationMode: import("@langchain/langgraph").BaseChannel<"
|
|
26
|
+
operationMode: import("@langchain/langgraph").BaseChannel<"update" | "create" | "delete" | "read", "update" | "create" | "delete" | "read" | import("@langchain/langgraph").OverwriteValue<"update" | "create" | "delete" | "read">, unknown>;
|
|
27
27
|
/** For create mode: network creation data. */
|
|
28
28
|
createInput: import("@langchain/langgraph").BaseChannel<{
|
|
29
29
|
title: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Cache } from '../../shared/interfaces/cache.interface.js';
|
|
2
|
-
import type { OpportunityPresenter, PresenterDatabase } from './opportunity.
|
|
2
|
+
import type { OpportunityPresenter, PresenterDatabase } from './opportunity.presentation.js';
|
|
3
3
|
export interface CachedDeliveryCard {
|
|
4
4
|
opportunityId: string;
|
|
5
5
|
headline: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { gatherPresenterContext } from './opportunity.
|
|
2
|
-
import { buildDeliveryCardPresentationCacheKey } from '
|
|
1
|
+
import { gatherPresenterContext } from './opportunity.presentation.js';
|
|
2
|
+
import { buildDeliveryCardPresentationCacheKey } from './opportunity.presentation.js';
|
|
3
3
|
export const DELIVERY_CARD_CACHE_TTL = 24 * 60 * 60; // 24 hours
|
|
4
4
|
export async function getOrCreateDeliveryCardBatch(cache, presenter, presenterDb, opportunities, viewerId, options) {
|
|
5
5
|
if (opportunities.length === 0) {
|
|
@@ -38,11 +38,11 @@ export * from "./opportunity.persistence-admission.js";
|
|
|
38
38
|
export * from "./opportunity.update-admission.js";
|
|
39
39
|
export * from "./opportunity.newborn-stamping.js";
|
|
40
40
|
export * from "./opportunity.evaluator.js";
|
|
41
|
-
export * from "./opportunity.
|
|
41
|
+
export * from "./opportunity.presentation.js";
|
|
42
42
|
export * from "./opportunity.introducer.js";
|
|
43
43
|
export * from "./opportunity.existing-negotiation.js";
|
|
44
44
|
export * from "./opportunity.graph.js";
|
|
45
|
-
export * from "./opportunity.
|
|
45
|
+
export * from "./opportunity.presentation.js";
|
|
46
46
|
export * from "./opportunity.discovery-negotiation-summary.js";
|
|
47
47
|
export * from "./negotiation-context.loader.js";
|
|
48
48
|
export * from "./opportunity.feed-selection.js";
|
|
@@ -39,11 +39,11 @@ export * from "./opportunity.persistence-admission.js";
|
|
|
39
39
|
export * from "./opportunity.update-admission.js";
|
|
40
40
|
export * from "./opportunity.newborn-stamping.js";
|
|
41
41
|
export * from "./opportunity.evaluator.js";
|
|
42
|
-
export * from "./opportunity.
|
|
42
|
+
export * from "./opportunity.presentation.js";
|
|
43
43
|
export * from "./opportunity.introducer.js";
|
|
44
44
|
export * from "./opportunity.existing-negotiation.js";
|
|
45
45
|
export * from "./opportunity.graph.js";
|
|
46
|
-
export * from "./opportunity.
|
|
46
|
+
export * from "./opportunity.presentation.js";
|
|
47
47
|
export * from "./opportunity.discovery-negotiation-summary.js";
|
|
48
48
|
export * from "./negotiation-context.loader.js";
|
|
49
49
|
export * from "./opportunity.feed-selection.js";
|
|
@@ -12,7 +12,7 @@ import { tool } from "@langchain/core/tools";
|
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { protocolLogger } from "../../shared/observability/protocol.logger.js";
|
|
14
14
|
import { Timed } from "../../shared/observability/performance.js";
|
|
15
|
-
import { stripUuids } from "
|
|
15
|
+
import { stripUuids } from "./opportunity.presentation.js";
|
|
16
16
|
import { createStructuredModel } from "../../shared/agent/model.config.js";
|
|
17
17
|
import { invokeWithAbortSignal } from "../../shared/agent/model-signal.js";
|
|
18
18
|
import { renderOpportunityEvidenceForPrompt } from '../domain/opportunity.evidence.js';
|