@indexnetwork/protocol 20.0.1-rc.485.1 → 21.0.0-rc.486.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 +24 -0
- package/IMPLEMENTATION.md +1 -1
- package/dist/agents/index.d.ts +0 -1
- package/dist/agents/index.js +0 -1
- package/dist/chat/chat.agent.d.ts +0 -6
- package/dist/chat/chat.agent.js +0 -94
- package/dist/chat/chat.persona.d.ts +1 -2
- package/dist/chat/chat.prompt.js +1 -1
- package/dist/chat/chat.prompt.modules.d.ts +0 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/shared/agent/tool.factory.js +0 -1
- package/dist/shared/agent/tool.helpers.d.ts +0 -8
- package/dist/shared/agent/tool.helpers.js +1 -2
- package/package.json +1 -1
- package/dist/chat/reporter.action.contracts.d.ts +0 -36
- package/dist/chat/reporter.action.contracts.js +0 -1
- package/dist/chat/reporter.action.tools.d.ts +0 -17
- package/dist/chat/reporter.action.tools.js +0 -145
- package/dist/chat/reporter.persona.d.ts +0 -31
- package/dist/chat/reporter.persona.js +0 -207
- package/dist/chat/reporter.prompt.d.ts +0 -24
- package/dist/chat/reporter.prompt.js +0 -149
package/CHANGELOG.md
CHANGED
|
@@ -20,6 +20,30 @@ went 6.7.1 → 8.0.2 with no 7.x in between because the whole 7.x line shipped a
|
|
|
20
20
|
prereleases between the two promotions. To track every change, read `rc`; to
|
|
21
21
|
pin a supported release, use `latest`.
|
|
22
22
|
|
|
23
|
+
## 21.0.0 - 2026-08-17
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- **Breaking:** remove the Agent reporter persona. `REPORTER_PERSONA`,
|
|
28
|
+
`REPORTER_PERSONA_ID`, `REPORTER_BRIEFING_KICKOFF`, `REPORTER_TOOL_NAMES`,
|
|
29
|
+
`createReporterTools`, `filterReporterTools` and `narrowReporterTools` are
|
|
30
|
+
gone from the package entry point, along with `reporter.persona.ts`,
|
|
31
|
+
`reporter.prompt.ts` and the `propose_cleanup_actions` tool
|
|
32
|
+
(`reporter.action.tools.ts`, `reporter.action.contracts.ts`). Hosts that named
|
|
33
|
+
the `reporter` persona must pick another; unknown personas already fail closed.
|
|
34
|
+
- **Breaking:** drop `actionToolsEnabled` from `ToolContext`/`ToolDeps`/
|
|
35
|
+
`ResolvedToolContext` and `actionProposalStore` from `ToolDeps`. Both existed
|
|
36
|
+
solely to gate the reporter's cleanup-action tool. `resolveChatContext()` no
|
|
37
|
+
longer accepts or returns `actionToolsEnabled`.
|
|
38
|
+
- Remove `ChatAgent.hasPriorAgentActionProposal()`, the
|
|
39
|
+
```agent_action_proposal``` fence parser behind it, and
|
|
40
|
+
`IterationContext.hasPriorAgentActionProposal`. The reporter prompt was the
|
|
41
|
+
only consumer, so all of it became dead with the persona.
|
|
42
|
+
|
|
43
|
+
`read_activity_summary` and the whole `activity-projection` module are
|
|
44
|
+
**unchanged** — they are shared utility tools registered for every persona and
|
|
45
|
+
exposed on the MCP server, not reporter-specific.
|
|
46
|
+
|
|
23
47
|
## 20.0.1 - 2026-08-17
|
|
24
48
|
|
|
25
49
|
### Changed
|
package/IMPLEMENTATION.md
CHANGED
|
@@ -173,7 +173,7 @@ The intent and community graphs are the exceptions: they are reached through the
|
|
|
173
173
|
|
|
174
174
|
### Persisted chat personas
|
|
175
175
|
|
|
176
|
-
`ChatGraphFactory.withPersona()` keeps the runtime neutral while selecting an exported persona configuration. `SIGNAL_PERSONA
|
|
176
|
+
`ChatGraphFactory.withPersona()` keeps the runtime neutral while selecting an exported persona configuration. `SIGNAL_PERSONA` and `ONBOARDING_PERSONA` each own an exact positive tool allowlist; shared tools added later remain unavailable until reviewed. `ONBOARDING_PERSONA` reuses Signal's proposal-only, live-membership-narrowed `create_intent` contract and otherwise exposes only privacy consent, approved self-context preview/confirmation, guided questions, and validated completion. Hosts must persist the exported persona ID on session creation and treat it as authoritative on follow-ups.
|
|
177
177
|
|
|
178
178
|
## Intents
|
|
179
179
|
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -15,7 +15,6 @@ export { ChatTitleGenerator, } from "../chat/chat.title.generator.js";
|
|
|
15
15
|
export { createChatTools, } from "../chat/chat.tools.js";
|
|
16
16
|
export { createNegotiatorPersona, NEGOTIATOR_PERSONA_ID, } from "../chat/negotiator.persona.js";
|
|
17
17
|
export { createOnboardingTools, filterOnboardingTools, narrowOnboardingTools, ONBOARDING_PERSONA, ONBOARDING_PERSONA_ID, ONBOARDING_PROFILE_KICKOFF, ONBOARDING_TOOL_NAMES, } from "../chat/onboarding.persona.js";
|
|
18
|
-
export { createReporterTools, filterReporterTools, narrowReporterTools, REPORTER_BRIEFING_KICKOFF, REPORTER_PERSONA, REPORTER_PERSONA_ID, REPORTER_TOOL_NAMES, } from "../chat/reporter.persona.js";
|
|
19
18
|
export { createSignalTools, filterSignalTools, narrowSignalTools, SIGNAL_NEW_SIGNAL_KICKOFF, SIGNAL_PERSONA, SIGNAL_PERSONA_ID, SIGNAL_TOOL_NAMES, } from "../chat/signal.persona.js";
|
|
20
19
|
export { createAgentTools, } from "./application/index.js";
|
|
21
20
|
export { SYSTEM_AGENT_IDS, } from "./domain/index.js";
|
package/dist/agents/index.js
CHANGED
|
@@ -6,7 +6,6 @@ export { ChatTitleGenerator, } from "../chat/chat.title.generator.js";
|
|
|
6
6
|
export { createChatTools, } from "../chat/chat.tools.js";
|
|
7
7
|
export { createNegotiatorPersona, NEGOTIATOR_PERSONA_ID, } from "../chat/negotiator.persona.js";
|
|
8
8
|
export { createOnboardingTools, filterOnboardingTools, narrowOnboardingTools, ONBOARDING_PERSONA, ONBOARDING_PERSONA_ID, ONBOARDING_PROFILE_KICKOFF, ONBOARDING_TOOL_NAMES, } from "../chat/onboarding.persona.js";
|
|
9
|
-
export { createReporterTools, filterReporterTools, narrowReporterTools, REPORTER_BRIEFING_KICKOFF, REPORTER_PERSONA, REPORTER_PERSONA_ID, REPORTER_TOOL_NAMES, } from "../chat/reporter.persona.js";
|
|
10
9
|
export { createSignalTools, filterSignalTools, narrowSignalTools, SIGNAL_NEW_SIGNAL_KICKOFF, SIGNAL_PERSONA, SIGNAL_PERSONA_ID, SIGNAL_TOOL_NAMES, } from "../chat/signal.persona.js";
|
|
11
10
|
export { createAgentTools, } from "./application/index.js";
|
|
12
11
|
export { SYSTEM_AGENT_IDS, } from "./domain/index.js";
|
|
@@ -195,12 +195,6 @@ export declare class ChatAgent {
|
|
|
195
195
|
* Extracts the text content of the most recent HumanMessage.
|
|
196
196
|
*/
|
|
197
197
|
private static getCurrentUserMessage;
|
|
198
|
-
/**
|
|
199
|
-
* Detects a prior visible action proposal without considering current-turn
|
|
200
|
-
* tool calls. The reporter uses this only to make typed confirmation
|
|
201
|
-
* language contextual rather than executable on its own.
|
|
202
|
-
*/
|
|
203
|
-
static hasPriorAgentActionProposal(messages: BaseMessage[]): boolean;
|
|
204
198
|
/**
|
|
205
199
|
* Async factory: creates a ChatAgent with resolved user/index context.
|
|
206
200
|
* Resolves user/network identity from DB during tool initialization.
|
package/dist/chat/chat.agent.js
CHANGED
|
@@ -19,55 +19,6 @@ import { sanitizeForDebugMeta } from "../shared/observability/debug-meta.sanitiz
|
|
|
19
19
|
import { Timed } from "../shared/observability/performance.js";
|
|
20
20
|
import { requestContext } from "../shared/observability/request-context.js";
|
|
21
21
|
import { deduplicateQuestions } from "./chat.question-dedup.js";
|
|
22
|
-
const AGENT_ACTION_PROPOSAL_FENCE_PATTERN = /```agent_action_proposal\s*\n([\s\S]*?)\n```/g;
|
|
23
|
-
const AGENT_ACTION_PROPOSAL_KEYS = new Set(["proposalId", "actions"]);
|
|
24
|
-
const AGENT_ACTION_KEYS = new Set([
|
|
25
|
-
"type",
|
|
26
|
-
"entityId",
|
|
27
|
-
"currentState",
|
|
28
|
-
"proposedOperation",
|
|
29
|
-
"skipped",
|
|
30
|
-
"reason",
|
|
31
|
-
"description",
|
|
32
|
-
"evidence",
|
|
33
|
-
]);
|
|
34
|
-
const AGENT_ACTION_OPERATIONS = {
|
|
35
|
-
retract_premise: "RETRACT_PREMISE",
|
|
36
|
-
narrow_signal: "NARROW_SIGNAL",
|
|
37
|
-
pause_signal: "PAUSE_SIGNAL",
|
|
38
|
-
};
|
|
39
|
-
function isVisibleAgentActionProposal(payload) {
|
|
40
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
41
|
-
return false;
|
|
42
|
-
const proposal = payload;
|
|
43
|
-
if (Object.keys(proposal).some((key) => !AGENT_ACTION_PROPOSAL_KEYS.has(key)))
|
|
44
|
-
return false;
|
|
45
|
-
if (typeof proposal.proposalId !== "string"
|
|
46
|
-
|| !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(proposal.proposalId)
|
|
47
|
-
|| !Array.isArray(proposal.actions)
|
|
48
|
-
|| proposal.actions.length < 1
|
|
49
|
-
|| proposal.actions.length > 5)
|
|
50
|
-
return false;
|
|
51
|
-
return proposal.actions.every((rawAction) => {
|
|
52
|
-
if (!rawAction || typeof rawAction !== "object" || Array.isArray(rawAction))
|
|
53
|
-
return false;
|
|
54
|
-
const action = rawAction;
|
|
55
|
-
if (Object.keys(action).some((key) => !AGENT_ACTION_KEYS.has(key)))
|
|
56
|
-
return false;
|
|
57
|
-
if (typeof action.type !== "string" || !(action.type in AGENT_ACTION_OPERATIONS))
|
|
58
|
-
return false;
|
|
59
|
-
const type = action.type;
|
|
60
|
-
if (action.proposedOperation !== AGENT_ACTION_OPERATIONS[type])
|
|
61
|
-
return false;
|
|
62
|
-
if (typeof action.entityId !== "string" || !action.entityId.trim())
|
|
63
|
-
return false;
|
|
64
|
-
if (typeof action.currentState !== "string" || !action.currentState.trim())
|
|
65
|
-
return false;
|
|
66
|
-
if (action.skipped !== undefined && typeof action.skipped !== "boolean")
|
|
67
|
-
return false;
|
|
68
|
-
return ["reason", "description", "evidence"].every((key) => action[key] === undefined || typeof action[key] === "string");
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
22
|
const logger = protocolLogger("ChatAgent");
|
|
72
23
|
// Re-export for external consumers
|
|
73
24
|
export { ITERATION_NUDGE } from "./chat.prompt.js";
|
|
@@ -143,48 +94,6 @@ export class ChatAgent {
|
|
|
143
94
|
}
|
|
144
95
|
return undefined;
|
|
145
96
|
}
|
|
146
|
-
/**
|
|
147
|
-
* Detects a prior visible action proposal without considering current-turn
|
|
148
|
-
* tool calls. The reporter uses this only to make typed confirmation
|
|
149
|
-
* language contextual rather than executable on its own.
|
|
150
|
-
*/
|
|
151
|
-
static hasPriorAgentActionProposal(messages) {
|
|
152
|
-
let currentHumanIndex = -1;
|
|
153
|
-
let previousHumanIndex = -1;
|
|
154
|
-
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
155
|
-
if (messages[index]._getType() !== "human")
|
|
156
|
-
continue;
|
|
157
|
-
if (currentHumanIndex < 0) {
|
|
158
|
-
currentHumanIndex = index;
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
previousHumanIndex = index;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (currentHumanIndex < 0 || previousHumanIndex < 0)
|
|
166
|
-
return false;
|
|
167
|
-
let latestAssistant;
|
|
168
|
-
for (let index = currentHumanIndex - 1; index > previousHumanIndex; index -= 1) {
|
|
169
|
-
if (messages[index]._getType() === "ai") {
|
|
170
|
-
latestAssistant = messages[index];
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (!latestAssistant || typeof latestAssistant.content !== "string")
|
|
175
|
-
return false;
|
|
176
|
-
AGENT_ACTION_PROPOSAL_FENCE_PATTERN.lastIndex = 0;
|
|
177
|
-
for (const match of latestAssistant.content.matchAll(AGENT_ACTION_PROPOSAL_FENCE_PATTERN)) {
|
|
178
|
-
try {
|
|
179
|
-
if (isVisibleAgentActionProposal(JSON.parse(match[1])))
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
// An invalid prior fence is not visible proposal context.
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
97
|
/**
|
|
189
98
|
* Async factory: creates a ChatAgent with resolved user/index context.
|
|
190
99
|
* Resolves user/network identity from DB during tool initialization.
|
|
@@ -204,7 +113,6 @@ export class ChatAgent {
|
|
|
204
113
|
userId: context.userId,
|
|
205
114
|
networkId: scopedNetworkId,
|
|
206
115
|
sessionId: context.sessionId,
|
|
207
|
-
actionToolsEnabled: context.actionToolsEnabled,
|
|
208
116
|
});
|
|
209
117
|
if (explicitScope.scopeType && explicitScope.scopeId) {
|
|
210
118
|
resolved.scopeType = explicitScope.scopeType;
|
|
@@ -224,7 +132,6 @@ export class ChatAgent {
|
|
|
224
132
|
const iterCtx = {
|
|
225
133
|
recentTools: extractRecentToolCalls(messages),
|
|
226
134
|
currentMessage: ChatAgent.getCurrentUserMessage(messages),
|
|
227
|
-
hasPriorAgentActionProposal: ChatAgent.hasPriorAgentActionProposal(messages),
|
|
228
135
|
ctx: this.resolvedContext,
|
|
229
136
|
};
|
|
230
137
|
const deterministicResponse = this.persona.resolveDeterministicResponse?.(this.resolvedContext, iterCtx);
|
|
@@ -583,7 +490,6 @@ export class ChatAgent {
|
|
|
583
490
|
const iterCtx = {
|
|
584
491
|
recentTools: extractRecentToolCalls(messages),
|
|
585
492
|
currentMessage: ChatAgent.getCurrentUserMessage(messages),
|
|
586
|
-
hasPriorAgentActionProposal: ChatAgent.hasPriorAgentActionProposal(messages),
|
|
587
493
|
ctx: this.resolvedContext,
|
|
588
494
|
};
|
|
589
495
|
const deterministicResponse = this.persona.resolveDeterministicResponse?.(this.resolvedContext, iterCtx);
|
|
@@ -9,8 +9,7 @@ export interface ChatPersonaLoopBehaviors {
|
|
|
9
9
|
* Detect hallucinated ```opportunity / ```intent_proposal code blocks in model
|
|
10
10
|
* text, auto-invoke the corresponding tool, and strip unbacked blocks from the
|
|
11
11
|
* final response. Only meaningful for personas whose toolset can legitimately
|
|
12
|
-
* produce those blocks — enabled for onboarding, signal and negotiator
|
|
13
|
-
* for reporter.
|
|
12
|
+
* produce those blocks — enabled for onboarding, signal and negotiator.
|
|
14
13
|
*/
|
|
15
14
|
hallucinationRecovery: boolean;
|
|
16
15
|
}
|
package/dist/chat/chat.prompt.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Persona-neutral prompt text owned by the agent loop rather than by any one
|
|
6
6
|
// persona. Each persona builds its own system prompt (see signal.prompt.ts,
|
|
7
|
-
// negotiator.prompt.ts,
|
|
7
|
+
// negotiator.prompt.ts, onboarding.prompt.ts); what remains
|
|
8
8
|
// here is only what the loop itself injects regardless of persona.
|
|
9
9
|
/**
|
|
10
10
|
* Nudge message injected after SOFT_ITERATION_LIMIT iterations.
|
|
@@ -11,8 +11,6 @@ export interface IterationContext {
|
|
|
11
11
|
}>;
|
|
12
12
|
/** Text of the latest user message (for regex matching). */
|
|
13
13
|
currentMessage?: string;
|
|
14
|
-
/** Whether an earlier assistant turn visibly contained a valid action proposal. */
|
|
15
|
-
hasPriorAgentActionProposal?: boolean;
|
|
16
14
|
/** Resolved tool context (user, profile, indexes, etc.). */
|
|
17
15
|
ctx: ResolvedToolContext;
|
|
18
16
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,6 @@ export { ChatGraphFactory } from "./agents/index.js";
|
|
|
35
35
|
export { type ChatPersonaConfig } from "./agents/index.js";
|
|
36
36
|
export { NEGOTIATOR_PERSONA_ID, createNegotiatorPersona } from "./agents/index.js";
|
|
37
37
|
export { SIGNAL_PERSONA_ID, SIGNAL_PERSONA, } from "./agents/index.js";
|
|
38
|
-
export { REPORTER_PERSONA_ID, REPORTER_PERSONA, REPORTER_BRIEFING_KICKOFF, } from "./agents/index.js";
|
|
39
38
|
export { ONBOARDING_PERSONA_ID, ONBOARDING_PERSONA, } from "./agents/index.js";
|
|
40
39
|
export { RadarGraphFactory } from "./opportunities/index.js";
|
|
41
40
|
export { HydeGraphFactory } from "./discovery/index.js";
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,6 @@ export { DEFAULT_NETWORK_ASSIGNMENT_THRESHOLD, resolveAssignmentNetworkScope, bu
|
|
|
31
31
|
export { ChatGraphFactory } from "./agents/index.js";
|
|
32
32
|
export { NEGOTIATOR_PERSONA_ID, createNegotiatorPersona } from "./agents/index.js";
|
|
33
33
|
export { SIGNAL_PERSONA_ID, SIGNAL_PERSONA, } from "./agents/index.js";
|
|
34
|
-
export { REPORTER_PERSONA_ID, REPORTER_PERSONA, REPORTER_BRIEFING_KICKOFF, } from "./agents/index.js";
|
|
35
34
|
export { ONBOARDING_PERSONA_ID, ONBOARDING_PERSONA, } from "./agents/index.js";
|
|
36
35
|
export { RadarGraphFactory } from "./opportunities/index.js";
|
|
37
36
|
export { HydeGraphFactory } from "./discovery/index.js";
|
|
@@ -50,7 +50,6 @@ export async function createChatTools(deps, preResolvedContext) {
|
|
|
50
50
|
userId: deps.userId,
|
|
51
51
|
networkId: explicitScope.scopeType === 'network' ? explicitScope.scopeId : deps.networkId,
|
|
52
52
|
sessionId: deps.sessionId,
|
|
53
|
-
actionToolsEnabled: deps.actionToolsEnabled,
|
|
54
53
|
}));
|
|
55
54
|
if (!preResolvedContext && explicitScope.scopeType && explicitScope.scopeId) {
|
|
56
55
|
resolvedContext.scopeType = explicitScope.scopeType;
|
|
@@ -98,8 +98,6 @@ export interface ResolvedToolContext {
|
|
|
98
98
|
* which are owner-trusted and receive the full owner view.
|
|
99
99
|
*/
|
|
100
100
|
mcpCaller?: McpActivityCaller;
|
|
101
|
-
/** True only when the gated reporter cleanup-action proposal tool is registered. */
|
|
102
|
-
actionToolsEnabled?: boolean;
|
|
103
101
|
}
|
|
104
102
|
/**
|
|
105
103
|
* Dependencies passed when creating tools for a user session.
|
|
@@ -137,8 +135,6 @@ interface ToolContextBindings {
|
|
|
137
135
|
intentQueue: IntentGraphQueue;
|
|
138
136
|
/** Contact management operations. */
|
|
139
137
|
contactService: ContactServiceAdapter;
|
|
140
|
-
/** True only when the gated reporter cleanup-action proposal tool is registered. */
|
|
141
|
-
actionToolsEnabled?: boolean;
|
|
142
138
|
/** Chat session reader for loading conversation history. */
|
|
143
139
|
chatSession: ChatSessionReader;
|
|
144
140
|
/** Read-through chat-session digest. Optional; consumers fall back to undefined `chatContext`. */
|
|
@@ -184,8 +180,6 @@ interface ToolContextBindings {
|
|
|
184
180
|
* the backend composition root; when absent the tool is not registered.
|
|
185
181
|
*/
|
|
186
182
|
chatQuestions?: ChatQuestionsHost;
|
|
187
|
-
/** Optional durable persistence for reporter cleanup-action proposals. */
|
|
188
|
-
actionProposalStore?: import('../../chat/reporter.action.contracts.js').AgentActionProposalStore;
|
|
189
183
|
/** Durable host persistence for verified intent proposals shown in chat. */
|
|
190
184
|
intentProposalStore?: import('../../intents/intent.proposal.js').IntentProposalStore;
|
|
191
185
|
/**
|
|
@@ -292,8 +286,6 @@ export declare function resolveChatContext(params: {
|
|
|
292
286
|
networkId?: string;
|
|
293
287
|
/** Chat session ID for draft opportunities (stored as context.conversationId). */
|
|
294
288
|
sessionId?: string;
|
|
295
|
-
/** Reporter action gate forwarded into the persona prompt/context. */
|
|
296
|
-
actionToolsEnabled?: boolean;
|
|
297
289
|
}): Promise<ResolvedToolContext>;
|
|
298
290
|
/**
|
|
299
291
|
* Type for the `defineTool` closure created in `createChatTools`.
|
|
@@ -16,7 +16,7 @@ export class ChatContextAccessError extends Error {
|
|
|
16
16
|
* This preloads user identity, profile, network memberships, and scoped index role.
|
|
17
17
|
*/
|
|
18
18
|
export async function resolveChatContext(params) {
|
|
19
|
-
const { database, userId, networkId, sessionId
|
|
19
|
+
const { database, userId, networkId, sessionId } = params;
|
|
20
20
|
const [user, rawProfile, userNetworks, globalContext] = await Promise.all([
|
|
21
21
|
database.getUser(userId),
|
|
22
22
|
database.getProfile(userId),
|
|
@@ -93,7 +93,6 @@ export async function resolveChatContext(params) {
|
|
|
93
93
|
scopedMembershipRole,
|
|
94
94
|
isOnboarding: !(user.onboarding?.completedAt),
|
|
95
95
|
hasName,
|
|
96
|
-
actionToolsEnabled,
|
|
97
96
|
...(sessionId !== undefined ? { sessionId } : {}),
|
|
98
97
|
};
|
|
99
98
|
}
|
package/package.json
CHANGED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export type AgentCleanupActionType = "retract_premise" | "narrow_signal" | "pause_signal";
|
|
2
|
-
export interface AgentActionProposalSnapshot {
|
|
3
|
-
status: string;
|
|
4
|
-
updatedAt?: string;
|
|
5
|
-
payload?: string;
|
|
6
|
-
summary?: string | null;
|
|
7
|
-
assertionText?: string;
|
|
8
|
-
}
|
|
9
|
-
interface AgentActionProposalActionBase {
|
|
10
|
-
entityId: string;
|
|
11
|
-
currentState: string;
|
|
12
|
-
proposedOperation: string;
|
|
13
|
-
evidence?: string;
|
|
14
|
-
skipped?: boolean;
|
|
15
|
-
reason?: string;
|
|
16
|
-
snapshot?: AgentActionProposalSnapshot;
|
|
17
|
-
description?: string;
|
|
18
|
-
}
|
|
19
|
-
export type AgentActionProposalAction = (AgentActionProposalActionBase & {
|
|
20
|
-
type: "retract_premise";
|
|
21
|
-
}) | (AgentActionProposalActionBase & {
|
|
22
|
-
type: "narrow_signal";
|
|
23
|
-
}) | (AgentActionProposalActionBase & {
|
|
24
|
-
type: "pause_signal";
|
|
25
|
-
});
|
|
26
|
-
export interface AgentActionProposal {
|
|
27
|
-
proposalId: string;
|
|
28
|
-
userId: string;
|
|
29
|
-
conversationId?: string;
|
|
30
|
-
actions: AgentActionProposalAction[];
|
|
31
|
-
}
|
|
32
|
-
/** Host persistence bridge for proposals; the reporter tool never mutates domain rows. */
|
|
33
|
-
export interface AgentActionProposalStore {
|
|
34
|
-
createProposal(proposal: AgentActionProposal): Promise<void>;
|
|
35
|
-
}
|
|
36
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ChatGraphCompositeDatabase, UserDatabase } from "../shared/interfaces/database.interface.js";
|
|
2
|
-
import type { ChatTools, ResolvedToolContext, ToolContext } from "../shared/agent/tool.factory.js";
|
|
3
|
-
import type { AgentActionProposalStore } from "./reporter.action.contracts.js";
|
|
4
|
-
export type { AgentActionProposal, AgentActionProposalAction, AgentActionProposalSnapshot, AgentActionProposalStore, AgentCleanupActionType, } from "./reporter.action.contracts.js";
|
|
5
|
-
export declare const AGENT_ACTION_PROPOSAL_FENCE = "agent_action_proposal";
|
|
6
|
-
/**
|
|
7
|
-
* Creates the reporter-only proposal tool. It validates owner state and records
|
|
8
|
-
* snapshots, but intentionally has no mutation dependency.
|
|
9
|
-
*/
|
|
10
|
-
export declare function createProposeCleanupActionsTool(input: {
|
|
11
|
-
database: ChatGraphCompositeDatabase;
|
|
12
|
-
userDb: UserDatabase;
|
|
13
|
-
context: ResolvedToolContext;
|
|
14
|
-
store: AgentActionProposalStore;
|
|
15
|
-
}): ChatTools[number];
|
|
16
|
-
/** Build the optional action tool from the host-provided ToolContext. */
|
|
17
|
-
export declare function createReporterActionTool(deps: ToolContext, context: ResolvedToolContext, userDb: UserDatabase): ChatTools[number] | null;
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { tool } from "@langchain/core/tools";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { success } from "../shared/agent/tool.helpers.js";
|
|
4
|
-
export const AGENT_ACTION_PROPOSAL_FENCE = "agent_action_proposal";
|
|
5
|
-
const actionInputSchema = z.discriminatedUnion("type", [
|
|
6
|
-
z.object({
|
|
7
|
-
type: z.literal("retract_premise"),
|
|
8
|
-
premiseId: z.string().trim().min(1),
|
|
9
|
-
}).strict(),
|
|
10
|
-
z.object({
|
|
11
|
-
type: z.literal("narrow_signal"),
|
|
12
|
-
intentId: z.string().trim().min(1),
|
|
13
|
-
description: z.string().trim().min(1),
|
|
14
|
-
}).strict(),
|
|
15
|
-
z.object({
|
|
16
|
-
type: z.literal("pause_signal"),
|
|
17
|
-
intentId: z.string().trim().min(1),
|
|
18
|
-
evidence: z.string().trim().min(1, "pause_signal evidence is required"),
|
|
19
|
-
}).strict(),
|
|
20
|
-
]);
|
|
21
|
-
const proposeCleanupActionsSchema = z.object({
|
|
22
|
-
actions: z.array(actionInputSchema).min(1).max(5),
|
|
23
|
-
}).strict();
|
|
24
|
-
function isFullUuid(value) {
|
|
25
|
-
return z.string().uuid().safeParse(value).success;
|
|
26
|
-
}
|
|
27
|
-
function skippedAction(action, entityId, proposedOperation, reason) {
|
|
28
|
-
return {
|
|
29
|
-
type: action.type,
|
|
30
|
-
entityId,
|
|
31
|
-
currentState: "UNKNOWN",
|
|
32
|
-
proposedOperation,
|
|
33
|
-
skipped: true,
|
|
34
|
-
reason,
|
|
35
|
-
...(action.type === "pause_signal" ? { evidence: action.evidence } : {}),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Creates the reporter-only proposal tool. It validates owner state and records
|
|
40
|
-
* snapshots, but intentionally has no mutation dependency.
|
|
41
|
-
*/
|
|
42
|
-
export function createProposeCleanupActionsTool(input) {
|
|
43
|
-
const { database, userDb, context, store } = input;
|
|
44
|
-
return tool(async (query) => {
|
|
45
|
-
const proposalId = crypto.randomUUID();
|
|
46
|
-
const actions = [];
|
|
47
|
-
for (const action of query.actions) {
|
|
48
|
-
const entityId = action.type === "retract_premise" ? action.premiseId : action.intentId;
|
|
49
|
-
const operation = action.type === "retract_premise"
|
|
50
|
-
? "RETRACT_PREMISE"
|
|
51
|
-
: action.type === "narrow_signal" ? "NARROW_SIGNAL" : "PAUSE_SIGNAL";
|
|
52
|
-
if (!isFullUuid(entityId)) {
|
|
53
|
-
actions.push(skippedAction(action, entityId, operation, "A full UUID is required; suffixes and ambiguous IDs are not accepted."));
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (action.type === "retract_premise") {
|
|
57
|
-
const premise = await database.getPremise(entityId).catch(() => null);
|
|
58
|
-
if (!premise || premise.userId !== context.userId) {
|
|
59
|
-
actions.push(skippedAction(action, entityId, operation, "Premise not found or not owned by the authenticated user."));
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (premise.status !== "ACTIVE") {
|
|
63
|
-
actions.push({
|
|
64
|
-
type: action.type,
|
|
65
|
-
entityId,
|
|
66
|
-
currentState: premise.status,
|
|
67
|
-
proposedOperation: operation,
|
|
68
|
-
skipped: true,
|
|
69
|
-
reason: `Premise is already ${premise.status}.`,
|
|
70
|
-
});
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
actions.push({
|
|
74
|
-
type: action.type,
|
|
75
|
-
entityId,
|
|
76
|
-
currentState: premise.status,
|
|
77
|
-
proposedOperation: operation,
|
|
78
|
-
snapshot: { status: premise.status, updatedAt: premise.updatedAt.toISOString(), assertionText: premise.assertion.text },
|
|
79
|
-
});
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
const intent = await userDb.getIntent(entityId).catch(() => null);
|
|
83
|
-
if (!intent) {
|
|
84
|
-
actions.push(skippedAction(action, entityId, operation, "Signal not found or not owned by the authenticated user."));
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
const status = intent.status ?? "ACTIVE";
|
|
88
|
-
if (intent.archivedAt || status === "FULFILLED" || status === "EXPIRED") {
|
|
89
|
-
actions.push({
|
|
90
|
-
type: action.type,
|
|
91
|
-
entityId,
|
|
92
|
-
currentState: intent.archivedAt ? "ARCHIVED" : status,
|
|
93
|
-
proposedOperation: operation,
|
|
94
|
-
...(action.type === "pause_signal" ? { evidence: action.evidence } : {}),
|
|
95
|
-
skipped: true,
|
|
96
|
-
reason: "Signal is archived or terminal and cannot be changed.",
|
|
97
|
-
});
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
actions.push({
|
|
101
|
-
type: action.type,
|
|
102
|
-
entityId,
|
|
103
|
-
currentState: status,
|
|
104
|
-
proposedOperation: operation,
|
|
105
|
-
...(action.type === "pause_signal" ? { evidence: action.evidence } : {}),
|
|
106
|
-
...(action.type === "narrow_signal" ? { description: action.description } : {}),
|
|
107
|
-
snapshot: {
|
|
108
|
-
status,
|
|
109
|
-
updatedAt: intent.updatedAt.toISOString(),
|
|
110
|
-
payload: intent.payload,
|
|
111
|
-
summary: intent.summary,
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
await store.createProposal({
|
|
116
|
-
proposalId,
|
|
117
|
-
userId: context.userId,
|
|
118
|
-
...(context.sessionId ? { conversationId: context.sessionId } : {}),
|
|
119
|
-
actions,
|
|
120
|
-
});
|
|
121
|
-
const renderedActions = actions.map(({ snapshot: _snapshot, ...entry }) => entry);
|
|
122
|
-
const block = `\`\`\`${AGENT_ACTION_PROPOSAL_FENCE}\n${JSON.stringify({ proposalId, actions: renderedActions })}\n\`\`\``;
|
|
123
|
-
return success({
|
|
124
|
-
proposed: true,
|
|
125
|
-
proposalId,
|
|
126
|
-
actions: renderedActions,
|
|
127
|
-
message: `IMPORTANT: Include this \`\`\`agent_action_proposal code block EXACTLY as-is in your response. It is a REQUEST for owner confirmation; it performs no mutation until the owner confirms it in the UI:\n\n${block}`,
|
|
128
|
-
});
|
|
129
|
-
}, {
|
|
130
|
-
name: "propose_cleanup_actions",
|
|
131
|
-
description: "Prepare owner-confirmed cleanup actions from same-turn grounded reads. This tool never mutates data.",
|
|
132
|
-
schema: proposeCleanupActionsSchema,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
/** Build the optional action tool from the host-provided ToolContext. */
|
|
136
|
-
export function createReporterActionTool(deps, context, userDb) {
|
|
137
|
-
if (!deps.actionToolsEnabled || !deps.actionProposalStore)
|
|
138
|
-
return null;
|
|
139
|
-
return createProposeCleanupActionsTool({
|
|
140
|
-
database: deps.database,
|
|
141
|
-
userDb,
|
|
142
|
-
context,
|
|
143
|
-
store: deps.actionProposalStore,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { type ChatTools, type ResolvedToolContext, type ToolContext } from "../shared/agent/tool.factory.js";
|
|
2
|
-
import type { UserDatabase } from "../shared/interfaces/database.interface.js";
|
|
3
|
-
import type { ChatPersonaConfig } from "./chat.persona.js";
|
|
4
|
-
/** Public kickoff marker used by the Agent surface to request its opening briefing. */
|
|
5
|
-
export { REPORTER_BRIEFING_KICKOFF } from "./reporter.prompt.js";
|
|
6
|
-
/** Stable persona id persisted for read-only Agent reporting sessions. */
|
|
7
|
-
export declare const REPORTER_PERSONA_ID = "reporter";
|
|
8
|
-
/**
|
|
9
|
-
* Exact positive allowlist for the reporter persona. New shared tools remain
|
|
10
|
-
* unavailable until they are explicitly reviewed here.
|
|
11
|
-
*/
|
|
12
|
-
export declare const REPORTER_TOOL_NAMES: readonly ["read_intents", "search_intents", "read_user_contexts", "preview_user_context", "read_premises", "read_networks", "read_network_memberships", "read_pending_questions", "list_opportunities", "read_activity_summary"];
|
|
13
|
-
interface ReporterToolBoundary {
|
|
14
|
-
context: ResolvedToolContext;
|
|
15
|
-
userDb: UserDatabase;
|
|
16
|
-
findPendingQuestions?: ToolContext["findPendingQuestions"];
|
|
17
|
-
}
|
|
18
|
-
/** Filters a shared registry through the reporter's positive allowlist. */
|
|
19
|
-
export declare function filterReporterTools<T extends {
|
|
20
|
-
name: string;
|
|
21
|
-
}>(tools: T[]): T[];
|
|
22
|
-
/**
|
|
23
|
-
* Replaces shared tools whose normal modes can enumerate other users with
|
|
24
|
-
* reporter-safe, self-only read contracts. Opportunity listing is deliberately
|
|
25
|
-
* aggregate-only: it never returns a counterpart name, row, or explanation.
|
|
26
|
-
*/
|
|
27
|
-
export declare function narrowReporterTools(allowed: ChatTools, boundary: ReporterToolBoundary): ChatTools;
|
|
28
|
-
/** Creates the reporter's context-bound, allowlisted toolset. */
|
|
29
|
-
export declare function createReporterTools(deps: ToolContext, preResolvedContext?: ResolvedToolContext): Promise<ChatTools>;
|
|
30
|
-
/** Restricted read-only Agent reporter persona. */
|
|
31
|
-
export declare const REPORTER_PERSONA: ChatPersonaConfig;
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { tool } from "@langchain/core/tools";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { createChatTools } from "../shared/agent/tool.factory.js";
|
|
4
|
-
import { error, resolveChatContext, success } from "../shared/agent/tool.helpers.js";
|
|
5
|
-
import { focusedNetworkId, scopeFromNetworkId } from "../shared/agent/tool.scope.js";
|
|
6
|
-
import { buildReporterSystemContent, resolveReporterDeterministicResponse } from "./reporter.prompt.js";
|
|
7
|
-
import { createReporterActionTool } from "./reporter.action.tools.js";
|
|
8
|
-
/** Public kickoff marker used by the Agent surface to request its opening briefing. */
|
|
9
|
-
export { REPORTER_BRIEFING_KICKOFF } from "./reporter.prompt.js";
|
|
10
|
-
/** Stable persona id persisted for read-only Agent reporting sessions. */
|
|
11
|
-
export const REPORTER_PERSONA_ID = "reporter";
|
|
12
|
-
/**
|
|
13
|
-
* Exact positive allowlist for the reporter persona. New shared tools remain
|
|
14
|
-
* unavailable until they are explicitly reviewed here.
|
|
15
|
-
*/
|
|
16
|
-
export const REPORTER_TOOL_NAMES = [
|
|
17
|
-
"read_intents",
|
|
18
|
-
"search_intents",
|
|
19
|
-
"read_user_contexts",
|
|
20
|
-
"preview_user_context",
|
|
21
|
-
"read_premises",
|
|
22
|
-
"read_networks",
|
|
23
|
-
"read_network_memberships",
|
|
24
|
-
"read_pending_questions",
|
|
25
|
-
"list_opportunities",
|
|
26
|
-
"read_activity_summary",
|
|
27
|
-
];
|
|
28
|
-
const REPORTER_TOOL_ALLOWLIST = new Set(REPORTER_TOOL_NAMES);
|
|
29
|
-
/** Filters a shared registry through the reporter's positive allowlist. */
|
|
30
|
-
export function filterReporterTools(tools) {
|
|
31
|
-
return tools.filter((candidate) => REPORTER_TOOL_ALLOWLIST.has(candidate.name));
|
|
32
|
-
}
|
|
33
|
-
function invokeSharedTool(sharedTool, input) {
|
|
34
|
-
return sharedTool.invoke(input);
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Replaces shared tools whose normal modes can enumerate other users with
|
|
38
|
-
* reporter-safe, self-only read contracts. Opportunity listing is deliberately
|
|
39
|
-
* aggregate-only: it never returns a counterpart name, row, or explanation.
|
|
40
|
-
*/
|
|
41
|
-
export function narrowReporterTools(allowed, boundary) {
|
|
42
|
-
const { context, userDb } = boundary;
|
|
43
|
-
return allowed.map((sharedTool) => {
|
|
44
|
-
if (sharedTool.name === "read_intents") {
|
|
45
|
-
return tool(async (query) => {
|
|
46
|
-
const limit = query.limit ?? 100;
|
|
47
|
-
const page = query.page ?? 1;
|
|
48
|
-
const intents = await userDb.getActiveIntents();
|
|
49
|
-
const start = (page - 1) * limit;
|
|
50
|
-
return success({
|
|
51
|
-
intents: intents.slice(start, start + limit),
|
|
52
|
-
page,
|
|
53
|
-
limit,
|
|
54
|
-
total: intents.length,
|
|
55
|
-
});
|
|
56
|
-
}, {
|
|
57
|
-
name: "read_intents",
|
|
58
|
-
description: "Read the authenticated user's own active signals only.",
|
|
59
|
-
schema: z.object({
|
|
60
|
-
limit: z.number().int().min(1).max(100).optional(),
|
|
61
|
-
page: z.number().int().min(1).optional(),
|
|
62
|
-
}).strict(),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
if (sharedTool.name === "search_intents") {
|
|
66
|
-
return tool(async (query) => success({
|
|
67
|
-
intents: await userDb.searchOwnIntents(query.query, query.limit ?? 20),
|
|
68
|
-
}), {
|
|
69
|
-
name: "search_intents",
|
|
70
|
-
description: "Search the authenticated user's own active signals only.",
|
|
71
|
-
schema: z.object({
|
|
72
|
-
query: z.string().trim().min(1),
|
|
73
|
-
limit: z.number().int().min(1).max(100).optional(),
|
|
74
|
-
}).strict(),
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
if (sharedTool.name === "read_user_contexts") {
|
|
78
|
-
return tool(async () => invokeSharedTool(sharedTool, {}), {
|
|
79
|
-
name: "read_user_contexts",
|
|
80
|
-
description: "Read the authenticated user's own identity and global context only.",
|
|
81
|
-
schema: z.object({}).strict(),
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
if (sharedTool.name === "read_premises") {
|
|
85
|
-
return tool(async (query) => invokeSharedTool(sharedTool, {
|
|
86
|
-
includeRetracted: query.includeRetracted ?? false,
|
|
87
|
-
userId: context.userId,
|
|
88
|
-
}), {
|
|
89
|
-
name: "read_premises",
|
|
90
|
-
description: "Read the authenticated user's own premises only.",
|
|
91
|
-
schema: z.object({ includeRetracted: z.boolean().optional() }).strict(),
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
if (sharedTool.name === "read_network_memberships") {
|
|
95
|
-
return tool(async () => invokeSharedTool(sharedTool, {}), {
|
|
96
|
-
name: "read_network_memberships",
|
|
97
|
-
description: "Read the authenticated user's own network memberships only.",
|
|
98
|
-
schema: z.object({}).strict(),
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
if (sharedTool.name === "read_networks") {
|
|
102
|
-
return tool(async () => invokeSharedTool(sharedTool, { userId: context.userId }), {
|
|
103
|
-
name: "read_networks",
|
|
104
|
-
description: "Read networks available to the authenticated user.",
|
|
105
|
-
schema: z.object({}).strict(),
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
if (sharedTool.name === "read_pending_questions") {
|
|
109
|
-
return tool(async (query) => {
|
|
110
|
-
if (!boundary.findPendingQuestions)
|
|
111
|
-
return error("Question lookup is not available.");
|
|
112
|
-
const questions = await boundary.findPendingQuestions(context.userId, {
|
|
113
|
-
modes: ["intent"],
|
|
114
|
-
limit: query.limit ?? 10,
|
|
115
|
-
});
|
|
116
|
-
return success({ questions: questions.slice(0, query.limit ?? 10) });
|
|
117
|
-
}, {
|
|
118
|
-
name: "read_pending_questions",
|
|
119
|
-
description: "Read the user's own non-negotiation pending questions; answering is unavailable here.",
|
|
120
|
-
schema: z.object({ limit: z.number().int().min(1).max(10).optional() }).strict(),
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
if (sharedTool.name === "list_opportunities") {
|
|
124
|
-
return tool(async (query) => {
|
|
125
|
-
const scopedNetworkId = focusedNetworkId(context);
|
|
126
|
-
if (scopedNetworkId && query.networkId && query.networkId !== scopedNetworkId) {
|
|
127
|
-
return error("This chat is scoped to a different network.");
|
|
128
|
-
}
|
|
129
|
-
const activeIntents = await userDb.getActiveIntents();
|
|
130
|
-
const intentById = new Map(activeIntents.map((intent) => [intent.id, intent]));
|
|
131
|
-
const opportunities = await userDb.getOpportunitiesForUser({
|
|
132
|
-
...(query.networkId || scopedNetworkId ? { networkId: query.networkId || scopedNetworkId } : {}),
|
|
133
|
-
statuses: ["draft", "pending", "latent"],
|
|
134
|
-
limit: 100,
|
|
135
|
-
});
|
|
136
|
-
const counts = new Map();
|
|
137
|
-
const seen = new Set();
|
|
138
|
-
for (const opportunity of opportunities) {
|
|
139
|
-
const ownIntentIds = new Set(opportunity.actors
|
|
140
|
-
.filter((actor) => actor.userId === context.userId && actor.intent && intentById.has(actor.intent))
|
|
141
|
-
.map((actor) => actor.intent));
|
|
142
|
-
if (ownIntentIds.size === 0)
|
|
143
|
-
continue;
|
|
144
|
-
seen.add(opportunity.id);
|
|
145
|
-
for (const intentId of ownIntentIds) {
|
|
146
|
-
const intent = intentById.get(intentId);
|
|
147
|
-
if (!intent)
|
|
148
|
-
continue;
|
|
149
|
-
const existing = counts.get(intentId) ?? {
|
|
150
|
-
intentId,
|
|
151
|
-
title: intent.summary?.trim() || intent.payload,
|
|
152
|
-
count: 0,
|
|
153
|
-
};
|
|
154
|
-
existing.count += 1;
|
|
155
|
-
counts.set(intentId, existing);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return success({
|
|
159
|
-
found: seen.size > 0,
|
|
160
|
-
count: seen.size,
|
|
161
|
-
bySignal: [...counts.values()],
|
|
162
|
-
});
|
|
163
|
-
}, {
|
|
164
|
-
name: "list_opportunities",
|
|
165
|
-
description: "Report current opportunity counts by the user's own signal, without counterpart identities or rows.",
|
|
166
|
-
schema: z.object({ networkId: z.string().uuid().optional() }).strict(),
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
return sharedTool;
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
/** Creates the reporter's context-bound, allowlisted toolset. */
|
|
173
|
-
export async function createReporterTools(deps, preResolvedContext) {
|
|
174
|
-
const explicitScope = deps.scopeType && deps.scopeId
|
|
175
|
-
? { scopeType: deps.scopeType, scopeId: deps.scopeId }
|
|
176
|
-
: scopeFromNetworkId(deps.networkId);
|
|
177
|
-
const resolvedContext = preResolvedContext ?? await resolveChatContext({
|
|
178
|
-
database: deps.database,
|
|
179
|
-
userId: deps.userId,
|
|
180
|
-
networkId: explicitScope.scopeType === "network" ? explicitScope.scopeId : deps.networkId,
|
|
181
|
-
sessionId: deps.sessionId,
|
|
182
|
-
actionToolsEnabled: deps.actionToolsEnabled,
|
|
183
|
-
});
|
|
184
|
-
if (explicitScope.scopeType && explicitScope.scopeId) {
|
|
185
|
-
resolvedContext.scopeType = explicitScope.scopeType;
|
|
186
|
-
resolvedContext.scopeId = explicitScope.scopeId;
|
|
187
|
-
}
|
|
188
|
-
const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);
|
|
189
|
-
const allowed = filterReporterTools(await createChatTools(deps, resolvedContext));
|
|
190
|
-
const narrowed = narrowReporterTools(allowed, {
|
|
191
|
-
context: resolvedContext,
|
|
192
|
-
userDb,
|
|
193
|
-
findPendingQuestions: deps.findPendingQuestions,
|
|
194
|
-
});
|
|
195
|
-
const actionTool = createReporterActionTool(deps, resolvedContext, userDb);
|
|
196
|
-
return actionTool ? [...narrowed, actionTool] : narrowed;
|
|
197
|
-
}
|
|
198
|
-
/** Restricted read-only Agent reporter persona. */
|
|
199
|
-
export const REPORTER_PERSONA = {
|
|
200
|
-
id: REPORTER_PERSONA_ID,
|
|
201
|
-
buildSystemContent: (ctx, iterCtx) => buildReporterSystemContent(ctx, iterCtx),
|
|
202
|
-
createTools: (deps, preResolvedContext) => createReporterTools(deps, preResolvedContext),
|
|
203
|
-
resolveDeterministicResponse: (_ctx, iterCtx) => resolveReporterDeterministicResponse(iterCtx),
|
|
204
|
-
loopBehaviors: {
|
|
205
|
-
hallucinationRecovery: false,
|
|
206
|
-
},
|
|
207
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { ResolvedToolContext } from "../shared/agent/tool.factory.js";
|
|
2
|
-
import type { IterationContext } from "./chat.prompt.modules.js";
|
|
3
|
-
/** Stable marker used by the Agent surface to request its opening briefing. */
|
|
4
|
-
export declare const REPORTER_BRIEFING_KICKOFF = "reporter-briefing-kickoff";
|
|
5
|
-
/**
|
|
6
|
-
* Recognizes the explicit opening briefing marker without putting ordinary
|
|
7
|
-
* reporter conversations into briefing mode accidentally.
|
|
8
|
-
*
|
|
9
|
-
* @param message - Latest user message in the current turn
|
|
10
|
-
* @returns Whether this turn is the Agent-surface briefing kickoff
|
|
11
|
-
*/
|
|
12
|
-
export declare function isReporterBriefingKickoff(message?: string): boolean;
|
|
13
|
-
/** Recognizes a short acknowledgement only when a prior proposal is visible. */
|
|
14
|
-
export declare function isReporterActionConfirmation(message?: string, hasPriorProposal?: boolean): boolean;
|
|
15
|
-
/** Deterministic reporter response for contextual typed acknowledgements. */
|
|
16
|
-
export declare function resolveReporterDeterministicResponse(iterCtx: IterationContext): string | null;
|
|
17
|
-
/**
|
|
18
|
-
* Builds the read-only reporter persona prompt.
|
|
19
|
-
*
|
|
20
|
-
* @param ctx - Resolved authenticated user context
|
|
21
|
-
* @param iterCtx - Current agent-loop context used for briefing kickoff
|
|
22
|
-
* @returns Complete reporter system content
|
|
23
|
-
*/
|
|
24
|
-
export declare function buildReporterSystemContent(ctx: ResolvedToolContext, iterCtx?: IterationContext): string;
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/** Stable marker used by the Agent surface to request its opening briefing. */
|
|
2
|
-
export const REPORTER_BRIEFING_KICKOFF = "reporter-briefing-kickoff";
|
|
3
|
-
/**
|
|
4
|
-
* Recognizes the explicit opening briefing marker without putting ordinary
|
|
5
|
-
* reporter conversations into briefing mode accidentally.
|
|
6
|
-
*
|
|
7
|
-
* @param message - Latest user message in the current turn
|
|
8
|
-
* @returns Whether this turn is the Agent-surface briefing kickoff
|
|
9
|
-
*/
|
|
10
|
-
export function isReporterBriefingKickoff(message) {
|
|
11
|
-
const normalized = message?.trim().toLocaleLowerCase()
|
|
12
|
-
.replace(/[–—]/g, "-")
|
|
13
|
-
.replace(/^_+|_+$/g, "");
|
|
14
|
-
if (!normalized)
|
|
15
|
-
return false;
|
|
16
|
-
return normalized === REPORTER_BRIEFING_KICKOFF;
|
|
17
|
-
}
|
|
18
|
-
/** Recognizes a short acknowledgement only when a prior proposal is visible. */
|
|
19
|
-
export function isReporterActionConfirmation(message, hasPriorProposal = false) {
|
|
20
|
-
const normalized = message?.trim().toLocaleLowerCase()
|
|
21
|
-
.replace(/[.!?,;:]+$/g, "")
|
|
22
|
-
.replace(/\s+/g, " ");
|
|
23
|
-
if (!normalized || !hasPriorProposal)
|
|
24
|
-
return false;
|
|
25
|
-
return new Set([
|
|
26
|
-
"i confirm",
|
|
27
|
-
"confirm",
|
|
28
|
-
"confirm it",
|
|
29
|
-
"approve",
|
|
30
|
-
"approved",
|
|
31
|
-
"approve it",
|
|
32
|
-
"i approve",
|
|
33
|
-
"yes",
|
|
34
|
-
"yes i confirm",
|
|
35
|
-
"yes please",
|
|
36
|
-
"please do it",
|
|
37
|
-
"proceed",
|
|
38
|
-
"go ahead",
|
|
39
|
-
]).has(normalized);
|
|
40
|
-
}
|
|
41
|
-
/** Deterministic reporter response for contextual typed acknowledgements. */
|
|
42
|
-
export function resolveReporterDeterministicResponse(iterCtx) {
|
|
43
|
-
if (!isReporterActionConfirmation(iterCtx.currentMessage, iterCtx.hasPriorAgentActionProposal === true))
|
|
44
|
-
return null;
|
|
45
|
-
return "Use the visible proposal card's Confirm control to approve this request. I won't run or recreate it from a chat acknowledgement.";
|
|
46
|
-
}
|
|
47
|
-
function buildBriefingGuidance(iterCtx) {
|
|
48
|
-
if (!isReporterBriefingKickoff(iterCtx?.currentMessage))
|
|
49
|
-
return "";
|
|
50
|
-
return `
|
|
51
|
-
|
|
52
|
-
## Opening briefing
|
|
53
|
-
This is the Agent-surface briefing kickoff. Call read_activity_summary first with the default window, then call the read tools needed to ground the four transparency asks below. Present one concise briefing covering:
|
|
54
|
-
1. summarize all my signals;
|
|
55
|
-
2. what did you do today?;
|
|
56
|
-
3. how do I look to others?;
|
|
57
|
-
4. what should I sharpen?
|
|
58
|
-
Do not claim a metric unless it appears in a tool result from this turn. If a section has no grounded data, say that plainly rather than filling the gap.`;
|
|
59
|
-
}
|
|
60
|
-
function buildTurnGuidance(iterCtx) {
|
|
61
|
-
const currentMessage = iterCtx?.currentMessage;
|
|
62
|
-
const confirmation = isReporterActionConfirmation(currentMessage, iterCtx?.hasPriorAgentActionProposal === true);
|
|
63
|
-
return `
|
|
64
|
-
|
|
65
|
-
## Turn discipline
|
|
66
|
-
- The detailed four-section opening briefing is reserved for the exact reporter-briefing-kickoff marker. Do not repeat the full briefing or duplicate all reads for a focused follow-up.
|
|
67
|
-
- For every non-kickoff message, answer only the user's current request and perform only the reads needed to answer it.
|
|
68
|
-
${confirmation ? `- This message is only a natural-language acknowledgement. Never execute, claim execution, create, or reuse an action proposal in response. Do not call read_activity_summary or propose_cleanup_actions. Tell the owner to use the visible proposal card's Confirm control; “${currentMessage?.trim()}” is not endpoint confirmation.` : ""}`;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Builds the read-only reporter persona prompt.
|
|
72
|
-
*
|
|
73
|
-
* @param ctx - Resolved authenticated user context
|
|
74
|
-
* @param iterCtx - Current agent-loop context used for briefing kickoff
|
|
75
|
-
* @returns Complete reporter system content
|
|
76
|
-
*/
|
|
77
|
-
export function buildReporterSystemContent(ctx, iterCtx) {
|
|
78
|
-
const userContext = JSON.stringify(ctx.user, null, 2);
|
|
79
|
-
const profileContext = ctx.userProfile
|
|
80
|
-
? JSON.stringify(ctx.userProfile, null, 2)
|
|
81
|
-
: "null";
|
|
82
|
-
const membershipContext = JSON.stringify(ctx.userNetworks.map((network) => ({
|
|
83
|
-
id: network.networkId,
|
|
84
|
-
title: network.networkTitle,
|
|
85
|
-
isPersonal: network.isPersonal,
|
|
86
|
-
})), null, 2);
|
|
87
|
-
const roleGuidance = ctx.actionToolsEnabled
|
|
88
|
-
? "Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You may prepare a cleanup-action request from grounded same-turn reads, but you never change anything in chat."
|
|
89
|
-
: "Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You observe; you never change anything. Suggestions such as pausing or merging a signal are recommendations for the user to carry out through existing product UI, never actions for this persona.";
|
|
90
|
-
const mutationRule = ctx.actionToolsEnabled
|
|
91
|
-
? "- Never mutate data in chat. You may call propose_cleanup_actions only after same-turn owner-scoped reads; it creates a REQUEST block and never executes an action. The owner must confirm through the product UI."
|
|
92
|
-
: "- Never create, update, delete, confirm, answer, remember, forget, assign, discover, negotiate, scrape, or otherwise mutate data. Do not ask the user a question through a tool.";
|
|
93
|
-
const actionGuidance = ctx.actionToolsEnabled ? `
|
|
94
|
-
|
|
95
|
-
## Cleanup-action requests
|
|
96
|
-
- You may propose only retract_premise, narrow_signal, and pause_signal actions grounded in read results from this same turn.
|
|
97
|
-
- Resolve references through owner-scoped read tools and pass exact full UUIDs only; never use suffixes, guesses, or IDs from another user.
|
|
98
|
-
- pause_signal requires non-empty evidence recorded from this turn, such as zero live opportunities plus the owner's statement.
|
|
99
|
-
- The proposal block is a REQUEST for owner confirmation. Never narrate an action as completed and never mutate inside chat.
|
|
100
|
-
- Do not propose actions for counterparties or expose counterparty identity.
|
|
101
|
-
` : "";
|
|
102
|
-
return `You are Agent, the user's private read-only activity reporter for ${ctx.userName}.
|
|
103
|
-
|
|
104
|
-
${roleGuidance}${actionGuidance}
|
|
105
|
-
|
|
106
|
-
## Hard rules
|
|
107
|
-
- Every factual claim, number, status, or trend must come from a tool result in the current turn. Never invent, estimate, or reuse an unverified metric.
|
|
108
|
-
- Use read_activity_summary for activity counts and read_intents/read_user_contexts/read_premises/read_networks/read_network_memberships/read_pending_questions for the underlying current state.
|
|
109
|
-
- Counterparties are identity-free aggregate data only: never reveal names, IDs, transcripts, message text, or per-counterparty rows. Do not infer what another person thinks from a match or negotiation.
|
|
110
|
-
${mutationRule}
|
|
111
|
-
- Do not write observed behavior back as a preference or premise. The user decides whether to act on a suggestion.
|
|
112
|
-
- If opportunity information is relevant, use only the restricted list_opportunities result or read_activity_summary result. Do not expose raw evaluator reasoning, matchReason, or internal JSON. Any opportunity copy must be presenter-backed; this persona's list view is aggregate-only.
|
|
113
|
-
- Be transparent about missing data and the reporting window. Keep the response concise, calm, and useful without hype.
|
|
114
|
-
|
|
115
|
-
## Four transparency asks
|
|
116
|
-
Be ready to answer:
|
|
117
|
-
- “summarize all my signals” — read the user's own signals and describe their current themes without inventing a synthesis.
|
|
118
|
-
- “what did you do today?” — report only grounded activity counts from the requested window.
|
|
119
|
-
- “how do I look to others?” — describe only the user's own stored context and signals; do not claim access to private counterparty opinions.
|
|
120
|
-
- “what should I sharpen?” — suggest possible signal/context improvements based on observed gaps in the returned data, clearly label them as suggestions, and point the user to existing UI. Never apply them.
|
|
121
|
-
|
|
122
|
-
## Allowed capabilities
|
|
123
|
-
- Own signal reads: read_intents, search_intents.
|
|
124
|
-
- Own context reads: read_user_contexts, preview_user_context, read_premises.
|
|
125
|
-
- Own community context: read_networks, read_network_memberships.
|
|
126
|
-
- Own pending-question reads: read_pending_questions (never answer them).
|
|
127
|
-
- Aggregate activity reporting: read_activity_summary.
|
|
128
|
-
- Aggregate current opportunity reporting: list_opportunities (no counterpart identities or rows).${ctx.actionToolsEnabled ? "\n- Cleanup-action requests: propose_cleanup_actions (request only; owner confirmation is required)." : ""}
|
|
129
|
-
|
|
130
|
-
## Session identity (preloaded)
|
|
131
|
-
- User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}
|
|
132
|
-
|
|
133
|
-
### User record
|
|
134
|
-
\`\`\`json
|
|
135
|
-
${userContext}
|
|
136
|
-
\`\`\`
|
|
137
|
-
|
|
138
|
-
### User context
|
|
139
|
-
\`\`\`json
|
|
140
|
-
${profileContext}
|
|
141
|
-
\`\`\`
|
|
142
|
-
|
|
143
|
-
### Current memberships
|
|
144
|
-
\`\`\`json
|
|
145
|
-
${membershipContext}
|
|
146
|
-
\`\`\`
|
|
147
|
-
|
|
148
|
-
Only the identity, context, and membership metadata above are preloaded. Signals, premises, questions, and activity are not preloaded: call the appropriate read/report tool before describing them.${buildTurnGuidance(iterCtx)}${buildBriefingGuidance(iterCtx)}`;
|
|
149
|
-
}
|