@agent-native/core 0.153.4 → 0.153.6
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/corpus/templates/analytics/actions/account-deep-dive.ts +1 -0
- package/corpus/templates/analytics/actions/bigquery-query.ts +1 -0
- package/corpus/templates/analytics/actions/bigquery.ts +1 -0
- package/corpus/templates/analytics/actions/content-calendar-schema.ts +1 -0
- package/corpus/templates/analytics/actions/content-calendar.ts +1 -0
- package/corpus/templates/analytics/actions/gcloud.ts +1 -0
- package/corpus/templates/analytics/actions/get-data-program.ts +4 -0
- package/corpus/templates/analytics/actions/get-error-issue.ts +1 -0
- package/corpus/templates/analytics/actions/get-monitor-stats.ts +1 -0
- package/corpus/templates/analytics/actions/get-monitor.ts +1 -0
- package/corpus/templates/analytics/actions/get-session-replay-events.ts +1 -0
- package/corpus/templates/analytics/actions/get-session-replay-summary.ts +1 -0
- package/corpus/templates/analytics/actions/get-session-replay-timeline.ts +1 -0
- package/corpus/templates/analytics/actions/gong-calls.ts +1 -0
- package/corpus/templates/analytics/actions/gong-native-insights.ts +1 -0
- package/corpus/templates/analytics/actions/grafana.ts +1 -0
- package/corpus/templates/analytics/actions/hubspot-deals.ts +1 -0
- package/corpus/templates/analytics/actions/hubspot-metrics.ts +1 -0
- package/corpus/templates/analytics/actions/hubspot-pipelines.ts +1 -0
- package/corpus/templates/analytics/actions/hubspot-records.ts +1 -0
- package/corpus/templates/analytics/actions/jira-search.ts +1 -0
- package/corpus/templates/analytics/actions/jira.ts +1 -0
- package/corpus/templates/analytics/actions/list-error-issues.ts +1 -0
- package/corpus/templates/analytics/actions/list-monitors.ts +1 -0
- package/corpus/templates/analytics/actions/list-session-recordings.ts +1 -0
- package/corpus/templates/analytics/actions/match-error-issues.ts +1 -0
- package/corpus/templates/analytics/actions/prometheus.ts +1 -0
- package/corpus/templates/analytics/actions/pylon-issues.ts +1 -0
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -0
- package/corpus/templates/analytics/actions/query-dashboard-panel.ts +1 -0
- package/corpus/templates/analytics/actions/query-inbound-forms.ts +1 -0
- package/corpus/templates/analytics/actions/run-monitor-check.ts +1 -0
- package/corpus/templates/analytics/actions/sentry.ts +1 -0
- package/corpus/templates/analytics/actions/seo-blog-pages.ts +1 -0
- package/corpus/templates/analytics/actions/seo-page-keywords.ts +1 -0
- package/corpus/templates/analytics/actions/seo-top-keywords.ts +1 -0
- package/corpus/templates/analytics/actions/slack-messages.ts +1 -0
- package/corpus/templates/analytics/actions/stripe.ts +1 -0
- package/corpus/templates/analytics/actions/test-custom-api-connection.ts +1 -0
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +62 -47
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +27 -34
- package/dist/action.d.ts +13 -0
- package/dist/action.js +3 -0
- package/dist/agent/engine/tool-call-journal-seed.d.ts +27 -10
- package/dist/agent/engine/tool-call-journal-seed.js +63 -32
- package/dist/agent/production-agent.d.ts +40 -1
- package/dist/agent/production-agent.js +201 -62
- package/dist/agent/run-loop-with-resume.d.ts +1 -1
- package/dist/agent/run-loop-with-resume.js +49 -35
- package/dist/agent/run-manager.js +17 -11
- package/dist/agent/run-store.d.ts +16 -0
- package/dist/agent/run-store.js +51 -9
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.js +15 -2
- package/dist/client/RouteTransitionIndicator.js +1 -1
- package/dist/client/agent-chat-adapter.js +242 -282
- package/dist/client/sse-event-processor.d.ts +7 -0
- package/dist/client/sse-event-processor.js +28 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.js +2 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/github-repo-files.js +1 -0
- package/dist/provider-api/actions/provider-api.js +1 -0
- package/dist/provider-api/actions/staged-datasets.js +1 -0
- package/dist/provider-api/corpus-jobs.js +1 -0
- package/dist/server/action-discovery.js +2 -0
- package/dist/server/agent-chat-plugin.js +35 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +1 -1
|
@@ -414,6 +414,7 @@ export default defineAction({
|
|
|
414
414
|
http: { method: "GET" },
|
|
415
415
|
readOnly: true,
|
|
416
416
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
417
|
+
grounding: true,
|
|
417
418
|
run: async (args) => {
|
|
418
419
|
const trimmedQuery = args.query.trim();
|
|
419
420
|
const gaps: string[] = [];
|
|
@@ -56,6 +56,7 @@ export default defineAction({
|
|
|
56
56
|
.describe("Additional Cloud Monitoring filter expression"),
|
|
57
57
|
}),
|
|
58
58
|
http: { method: "GET" },
|
|
59
|
+
grounding: true,
|
|
59
60
|
run: async (args) => {
|
|
60
61
|
const credentials = await requireActionCredentials(
|
|
61
62
|
["BIGQUERY_PROJECT_ID", "GOOGLE_APPLICATION_CREDENTIALS_JSON"],
|
|
@@ -22,6 +22,10 @@ export default defineAction({
|
|
|
22
22
|
}),
|
|
23
23
|
http: { method: "GET" },
|
|
24
24
|
readOnly: true,
|
|
25
|
+
// Not `grounding`: this reads the stored program plus its last-run summary,
|
|
26
|
+
// and even `includeRows` replays rows cached by an earlier run rather than
|
|
27
|
+
// querying the source. The flag means evidence retrieved at call time, which
|
|
28
|
+
// is why `get-analysis` is excluded too.
|
|
25
29
|
run: async (args) => {
|
|
26
30
|
const ctx = getCredentialContext();
|
|
27
31
|
if (!ctx) throw new Error("No authenticated context for get-data-program.");
|
|
@@ -29,6 +29,7 @@ export default defineAction({
|
|
|
29
29
|
http: { method: "GET" },
|
|
30
30
|
readOnly: true,
|
|
31
31
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
32
|
+
grounding: true,
|
|
32
33
|
run: async (args) => {
|
|
33
34
|
return getErrorIssue(resolveScope(), args.id, {
|
|
34
35
|
eventsLimit: args.eventsLimit,
|
|
@@ -22,6 +22,7 @@ export default defineAction({
|
|
|
22
22
|
http: { method: "GET" },
|
|
23
23
|
readOnly: true,
|
|
24
24
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
25
|
+
grounding: true,
|
|
25
26
|
run: async (args) => {
|
|
26
27
|
return getSessionReplaySummary(args.recordingId, resolveScope());
|
|
27
28
|
},
|
|
@@ -32,6 +32,7 @@ export default defineAction({
|
|
|
32
32
|
http: { method: "GET" },
|
|
33
33
|
readOnly: true,
|
|
34
34
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
35
|
+
grounding: true,
|
|
35
36
|
run: async (args) => {
|
|
36
37
|
return getSessionReplayTimeline(args.recordingId, resolveScope(), {
|
|
37
38
|
eventLimit: args.eventLimit,
|
|
@@ -66,6 +66,7 @@ export default defineAction({
|
|
|
66
66
|
Boolean(operation && allowCreditRequest),
|
|
67
67
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
68
68
|
http: false,
|
|
69
|
+
grounding: true,
|
|
69
70
|
run: async ({ operation, allowCreditRequest, arguments: args }) => {
|
|
70
71
|
const tools = gongNativeTools(await listVisibleMcpTools());
|
|
71
72
|
if (!operation) {
|
|
@@ -36,6 +36,7 @@ export default defineAction({
|
|
|
36
36
|
to: z.string().optional().describe("Query end time in epoch ms"),
|
|
37
37
|
}),
|
|
38
38
|
readOnly: true,
|
|
39
|
+
grounding: true,
|
|
39
40
|
run: async (args) => {
|
|
40
41
|
const credentials = await requireActionCredentials(
|
|
41
42
|
["GRAFANA_URL", "GRAFANA_API_TOKEN"],
|
|
@@ -15,6 +15,7 @@ export default defineAction({
|
|
|
15
15
|
"Get computed HubSpot sales metrics: win rate, ACV, pipeline value, etc.",
|
|
16
16
|
schema: z.object({}),
|
|
17
17
|
http: { method: "GET" },
|
|
18
|
+
grounding: true,
|
|
18
19
|
run: async () => {
|
|
19
20
|
const [deals, pipelines] = await Promise.all([
|
|
20
21
|
getAllDeals(),
|
|
@@ -10,6 +10,7 @@ export default defineAction({
|
|
|
10
10
|
description: "Get HubSpot deal pipelines and their stages.",
|
|
11
11
|
schema: z.object({}),
|
|
12
12
|
http: { method: "GET" },
|
|
13
|
+
grounding: true,
|
|
13
14
|
run: async () => {
|
|
14
15
|
const allPipelines = await getDealPipelines();
|
|
15
16
|
const pipelines = getVisiblePipelines(allPipelines);
|
|
@@ -49,6 +49,7 @@ export default defineAction({
|
|
|
49
49
|
}),
|
|
50
50
|
http: { method: "GET" },
|
|
51
51
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
52
|
+
grounding: true,
|
|
52
53
|
run: async ({ objectType, query, properties, limit, after }) => {
|
|
53
54
|
const result = await searchHubSpotObjects({
|
|
54
55
|
objectType,
|
|
@@ -25,6 +25,7 @@ export default defineAction({
|
|
|
25
25
|
.describe("Comma-separated field names to include"),
|
|
26
26
|
}),
|
|
27
27
|
http: false,
|
|
28
|
+
grounding: true,
|
|
28
29
|
run: async (args) => {
|
|
29
30
|
const credentials = await requireActionCredentials(
|
|
30
31
|
["JIRA_BASE_URL", "JIRA_USER_EMAIL", "JIRA_API_TOKEN"],
|
|
@@ -66,6 +66,7 @@ export default defineAction({
|
|
|
66
66
|
.describe("Lookback days for mode=analytics"),
|
|
67
67
|
}),
|
|
68
68
|
http: { method: "GET" },
|
|
69
|
+
grounding: true,
|
|
69
70
|
run: async (args) => {
|
|
70
71
|
const credentials = await requireActionCredentials(
|
|
71
72
|
["JIRA_BASE_URL", "JIRA_USER_EMAIL", "JIRA_API_TOKEN"],
|
|
@@ -12,6 +12,7 @@ export default defineAction({
|
|
|
12
12
|
"List the current user's uptime monitors with their latest status, latency, and 24h/7d uptime percentage.",
|
|
13
13
|
schema: z.object({}),
|
|
14
14
|
http: { method: "GET" },
|
|
15
|
+
grounding: true,
|
|
15
16
|
run: async () => {
|
|
16
17
|
const email = getRequestUserEmail();
|
|
17
18
|
if (!email) throw new Error("no authenticated user");
|
|
@@ -27,6 +27,7 @@ export default defineAction({
|
|
|
27
27
|
agentTool: false,
|
|
28
28
|
toolCallable: true,
|
|
29
29
|
http: { method: "POST" },
|
|
30
|
+
grounding: true,
|
|
30
31
|
run: async ({ account, accounts, query, days, pageSize, maxPages }) => {
|
|
31
32
|
if (accounts) {
|
|
32
33
|
const response = (await executeProviderApiRequest({
|
|
@@ -69,6 +69,7 @@ export default defineAction({
|
|
|
69
69
|
// natural-language question and Analytics forms the query.
|
|
70
70
|
http: false,
|
|
71
71
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
72
|
+
grounding: true,
|
|
72
73
|
run: async (args) => {
|
|
73
74
|
const result = await queryFirstPartyAnalytics(args.sql, resolveScope(), {
|
|
74
75
|
cache: true,
|
|
@@ -14,6 +14,7 @@ export default defineAction({
|
|
|
14
14
|
id: z.string().describe("Monitor id to check now."),
|
|
15
15
|
}),
|
|
16
16
|
http: { method: "POST" },
|
|
17
|
+
grounding: true,
|
|
17
18
|
run: async ({ id }) => {
|
|
18
19
|
const email = getRequestUserEmail();
|
|
19
20
|
if (!email) throw new Error("no authenticated user");
|
|
@@ -44,6 +44,7 @@ export default defineAction({
|
|
|
44
44
|
.describe("Sentry stats category for mode=stats; defaults to error"),
|
|
45
45
|
}),
|
|
46
46
|
http: { method: "GET" },
|
|
47
|
+
grounding: true,
|
|
47
48
|
run: async (args) => {
|
|
48
49
|
const credentials = await requireActionCredentials(
|
|
49
50
|
["SENTRY_SERVER_TOKEN", "SENTRY_AUTH_TOKEN"],
|
|
@@ -10,6 +10,7 @@ export default defineAction({
|
|
|
10
10
|
description: "Get SEO metrics for all blog pages.",
|
|
11
11
|
schema: z.object({}),
|
|
12
12
|
http: { method: "GET" },
|
|
13
|
+
grounding: true,
|
|
13
14
|
run: async () => {
|
|
14
15
|
const pages = await getAllBlogPagesSeo();
|
|
15
16
|
return { pages, total: Object.keys(pages).length };
|
|
@@ -15,6 +15,7 @@ export default defineAction({
|
|
|
15
15
|
.describe("Blog page slug (e.g. micro-frontends)"),
|
|
16
16
|
}),
|
|
17
17
|
http: { method: "GET" },
|
|
18
|
+
grounding: true,
|
|
18
19
|
run: async (args) => {
|
|
19
20
|
if (!args.slug) return { error: "slug is required" };
|
|
20
21
|
const keywords = await getRankedKeywordsForPage(args.slug, 20);
|
|
@@ -16,6 +16,7 @@ export default defineAction({
|
|
|
16
16
|
.describe("Max keywords to return (default 500)"),
|
|
17
17
|
}),
|
|
18
18
|
http: { method: "GET" },
|
|
19
|
+
grounding: true,
|
|
19
20
|
run: async (args) => {
|
|
20
21
|
const limit = Math.min(args.limit ?? 500, 1000);
|
|
21
22
|
const keywords = await getAllTopBlogKeywords(limit);
|
|
@@ -13,45 +13,6 @@ const INJECTED_CONTEXT_BLOCKS = [
|
|
|
13
13
|
"response-guard",
|
|
14
14
|
];
|
|
15
15
|
|
|
16
|
-
export const DATA_QUERY_ACTIONS = new Set([
|
|
17
|
-
"account-deep-dive",
|
|
18
|
-
"bigquery",
|
|
19
|
-
"content-calendar",
|
|
20
|
-
"content-calendar-schema",
|
|
21
|
-
// First-party observability reads are grounded data for incident triage,
|
|
22
|
-
// even though they are not provider queries. Keep the final-response guard
|
|
23
|
-
// from replacing valid session/error/replay evidence with the generic
|
|
24
|
-
// "connect a source" fallback.
|
|
25
|
-
"get-error-issue",
|
|
26
|
-
"get-session-replay-events",
|
|
27
|
-
"get-session-replay-summary",
|
|
28
|
-
"get-session-replay-timeline",
|
|
29
|
-
"list-error-issues",
|
|
30
|
-
"list-session-recordings",
|
|
31
|
-
"match-error-issues",
|
|
32
|
-
"gcloud",
|
|
33
|
-
"gong-calls",
|
|
34
|
-
"gong-native-insights",
|
|
35
|
-
"grafana",
|
|
36
|
-
"hubspot-deals",
|
|
37
|
-
"hubspot-metrics",
|
|
38
|
-
"hubspot-pipelines",
|
|
39
|
-
"hubspot-records",
|
|
40
|
-
"jira",
|
|
41
|
-
"jira-search",
|
|
42
|
-
"provider-api-request",
|
|
43
|
-
"provider-corpus-job",
|
|
44
|
-
"query-staged-dataset",
|
|
45
|
-
"query-agent-native-analytics",
|
|
46
|
-
"query-inbound-forms",
|
|
47
|
-
"sentry",
|
|
48
|
-
"seo-blog-pages",
|
|
49
|
-
"seo-page-keywords",
|
|
50
|
-
"seo-top-keywords",
|
|
51
|
-
"slack-messages",
|
|
52
|
-
"stripe",
|
|
53
|
-
]);
|
|
54
|
-
|
|
55
16
|
export const CORPUS_SOURCE_ACTIONS = new Set([
|
|
56
17
|
"provider-api-request",
|
|
57
18
|
"provider-corpus-job",
|
|
@@ -127,8 +88,63 @@ function isToolName(name: string, expected: string): boolean {
|
|
|
127
88
|
return normalizeActionToolName(name) === expected;
|
|
128
89
|
}
|
|
129
90
|
|
|
130
|
-
|
|
131
|
-
|
|
91
|
+
let groundingActionNames: ReadonlySet<string> | null = null;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Read the action names whose definitions declare `grounding: true`.
|
|
95
|
+
*
|
|
96
|
+
* Takes the registry as an argument rather than importing
|
|
97
|
+
* `.generated/actions-registry` because that registry imports every action, and
|
|
98
|
+
* `save-analysis` imports this module — importing it back here would close an
|
|
99
|
+
* evaluation cycle. The agent-chat plugin passes it in once at module load.
|
|
100
|
+
*/
|
|
101
|
+
export function deriveGroundingActionNames(
|
|
102
|
+
registry: Record<string, unknown>,
|
|
103
|
+
): string[] {
|
|
104
|
+
return Object.entries(registry)
|
|
105
|
+
.filter(([, module]) => {
|
|
106
|
+
// Action modules reach the registry either as the module namespace or as
|
|
107
|
+
// an already-unwrapped definition, the same two shapes
|
|
108
|
+
// `loadActionsFromStaticRegistry` normalizes.
|
|
109
|
+
const candidate = module as
|
|
110
|
+
| { grounding?: boolean; default?: { grounding?: boolean } }
|
|
111
|
+
| undefined;
|
|
112
|
+
return (
|
|
113
|
+
candidate?.grounding === true || candidate?.default?.grounding === true
|
|
114
|
+
);
|
|
115
|
+
})
|
|
116
|
+
.map(([name]) => name);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Publish the derived set for the response guard to consult. */
|
|
120
|
+
export function registerGroundingActions(names: Iterable<string>): void {
|
|
121
|
+
groundingActionNames = new Set(
|
|
122
|
+
[...names].map((name) => normalizeActionToolName(name)),
|
|
123
|
+
);
|
|
124
|
+
// An empty set is never a real deployment: it means the installed core build
|
|
125
|
+
// predates `grounding` and dropped it from every definition. Registration
|
|
126
|
+
// runs at plugin module scope, so throwing here would take the whole server
|
|
127
|
+
// down for a response-guard heuristic; the failure is raised at first use
|
|
128
|
+
// instead, where it costs one turn.
|
|
129
|
+
if (groundingActionNames.size === 0) {
|
|
130
|
+
console.error(
|
|
131
|
+
"[analytics] no action declares grounding: true; the installed @agent-native/core cannot carry the flag, so the response guard is unusable",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isGroundingActionName(name: string): boolean {
|
|
137
|
+
if (!groundingActionNames) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
"grounding actions were never registered: the analytics response guard cannot tell a grounded turn from an ungrounded one",
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (groundingActionNames.size === 0) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
"no action declares grounding: true; the installed @agent-native/core cannot carry the flag",
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
return groundingActionNames.has(normalizeActionToolName(name));
|
|
132
148
|
}
|
|
133
149
|
|
|
134
150
|
function isDashboardConstructionActionName(name: string): boolean {
|
|
@@ -214,7 +230,7 @@ function getRunCodeBridgeToolNames(content: string | undefined): string[] {
|
|
|
214
230
|
|
|
215
231
|
function hasRunCodeDataQueryAttempt(content: string | undefined): boolean {
|
|
216
232
|
return getRunCodeBridgeToolNames(content).some(
|
|
217
|
-
(name) =>
|
|
233
|
+
(name) => isGroundingActionName(name) || isMcpDataSourceTool(name),
|
|
218
234
|
);
|
|
219
235
|
}
|
|
220
236
|
|
|
@@ -530,10 +546,9 @@ export function hasIncompleteDataEvidence(
|
|
|
530
546
|
const name = String(result.name ?? "");
|
|
531
547
|
if (
|
|
532
548
|
name &&
|
|
533
|
-
!
|
|
549
|
+
!isGroundingActionName(name) &&
|
|
534
550
|
!isMcpDataSourceTool(name) &&
|
|
535
|
-
!isToolName(name, "run-code")
|
|
536
|
-
!isToolName(name, "provider-api-request")
|
|
551
|
+
!isToolName(name, "run-code")
|
|
537
552
|
) {
|
|
538
553
|
return false;
|
|
539
554
|
}
|
|
@@ -594,7 +609,7 @@ export function hasDataQueryAttempt(
|
|
|
594
609
|
if (isToolName(name, "run-code")) {
|
|
595
610
|
return hasRunCodeDataQueryAttempt(result.content);
|
|
596
611
|
}
|
|
597
|
-
return
|
|
612
|
+
return isGroundingActionName(name) || isMcpDataSourceTool(name);
|
|
598
613
|
});
|
|
599
614
|
}
|
|
600
615
|
|
|
@@ -605,7 +620,7 @@ function isFailedDataQueryAttempt(result: {
|
|
|
605
620
|
}): boolean {
|
|
606
621
|
const name = String(result.name ?? "");
|
|
607
622
|
const isDataQuery =
|
|
608
|
-
|
|
623
|
+
isGroundingActionName(name) ||
|
|
609
624
|
isMcpDataSourceTool(name) ||
|
|
610
625
|
(isToolName(name, "run-code") &&
|
|
611
626
|
hasRunCodeDataQueryAttempt(result.content));
|
|
@@ -12,6 +12,7 @@ import { ANALYTICS_CONNECTOR_CATALOG } from "../lib/analytics-connector-catalog"
|
|
|
12
12
|
import { credentialProviderConfigs } from "../lib/credential-keys";
|
|
13
13
|
import { isProductionServerlessRuntime } from "../lib/production-serverless-runtime.js";
|
|
14
14
|
import {
|
|
15
|
+
deriveGroundingActionNames,
|
|
15
16
|
draftClaimsAnalyticsMetrics,
|
|
16
17
|
failedDataQueryAttemptMessage,
|
|
17
18
|
hasDashboardConstructionAttempt,
|
|
@@ -29,8 +30,14 @@ import {
|
|
|
29
30
|
looksLikeAnalyticsDataRequest,
|
|
30
31
|
needsCorpusWorkflowForCoverageSensitiveRequest,
|
|
31
32
|
needsSourceRecordBodyWorkflowForCoverageSensitiveRequest,
|
|
33
|
+
registerGroundingActions,
|
|
32
34
|
} from "../lib/real-data-actions";
|
|
33
35
|
|
|
36
|
+
// Which actions return real data-source evidence is a fact each action states
|
|
37
|
+
// on itself (`grounding: true`). Reading it off the definitions here is what
|
|
38
|
+
// keeps the response guard from drifting behind a newly shipped source action.
|
|
39
|
+
registerGroundingActions(deriveGroundingActionNames(actionsRegistry));
|
|
40
|
+
|
|
34
41
|
const ANALYTICS_BACKGROUND_RUN_SOFT_TIMEOUT_MS = 13 * 60_000;
|
|
35
42
|
// A background job may legitimately spend minutes inside a provider/tool call,
|
|
36
43
|
// which the shared watchdog already excludes. Outside a tool call, however,
|
|
@@ -764,17 +771,23 @@ export function realDataFinalGuard(
|
|
|
764
771
|
}
|
|
765
772
|
|
|
766
773
|
if (dataQueryAttempted) return null;
|
|
774
|
+
const failedQueryMessage = failedDataQueryAttemptMessage(context.toolResults);
|
|
775
|
+
// Whether the built-in source is worth trying is decided by tool evidence,
|
|
776
|
+
// not by how the draft is worded, so it is asked once for every draft shape.
|
|
777
|
+
// A source that already failed this turn is not an untried source: steering
|
|
778
|
+
// the model back into it is how a permanently failing precondition turns
|
|
779
|
+
// into a retry loop.
|
|
780
|
+
if (firstPartySourceShouldBeTried && !failedQueryMessage) {
|
|
781
|
+
return {
|
|
782
|
+
retryMessage:
|
|
783
|
+
"The user asked for live analytics, and the built-in first-party Analytics source is available even though no external provider is connected. Call `query-agent-native-analytics` for first-party product, usage, conversion, or observability data and answer from that result. If the request specifically names an external provider, explain what is missing and include the real Connect data sources link.",
|
|
784
|
+
fallbackMessage:
|
|
785
|
+
"I couldn't complete a grounded first-party Analytics query yet. Please retry and I'll use the built-in Analytics source before asking you to connect an external provider.",
|
|
786
|
+
maxRetries: 2,
|
|
787
|
+
expandToolSurface: true,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
767
790
|
if (isSafeNoDataAnalyticsResponse(context.text)) {
|
|
768
|
-
if (firstPartySourceShouldBeTried) {
|
|
769
|
-
return {
|
|
770
|
-
retryMessage:
|
|
771
|
-
"The built-in first-party Analytics source is available even though no external provider is connected. Use `query-agent-native-analytics` for the user's first-party product, usage, conversion, or observability question before explaining that data is unavailable. Only guide the user to connect a source if the request specifically needs an external provider.",
|
|
772
|
-
fallbackMessage:
|
|
773
|
-
"I couldn't complete a grounded first-party Analytics query yet. Please retry and I'll use the built-in Analytics source before asking you to connect an external provider.",
|
|
774
|
-
maxRetries: 2,
|
|
775
|
-
expandToolSurface: true,
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
791
|
if (
|
|
779
792
|
needsDataSourceLink &&
|
|
780
793
|
!includesDataSourcesLink(context.text, setupLink)
|
|
@@ -787,7 +800,6 @@ export function realDataFinalGuard(
|
|
|
787
800
|
}
|
|
788
801
|
return null;
|
|
789
802
|
}
|
|
790
|
-
const failedQueryMessage = failedDataQueryAttemptMessage(context.toolResults);
|
|
791
803
|
if (failedQueryMessage) {
|
|
792
804
|
if (
|
|
793
805
|
needsDataSourceLink &&
|
|
@@ -805,11 +817,10 @@ export function realDataFinalGuard(
|
|
|
805
817
|
};
|
|
806
818
|
}
|
|
807
819
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
) {
|
|
820
|
+
// `needsDataSourceLink` already carries "status was checked, the user named an
|
|
821
|
+
// external source, and it is missing or nothing is connected". Restating those
|
|
822
|
+
// clauses here is what made the next branch look reachable.
|
|
823
|
+
if (needsDataSourceLink) {
|
|
813
824
|
return {
|
|
814
825
|
retryMessage: `The requested external source is not connected. Explain what is missing in the context of the user's question and include this exact markdown link: ${setupMarkdown}. Do not use the generic no-grounded-data fallback.`,
|
|
815
826
|
fallbackMessage: `I can help with that once the relevant source is connected. ${setupMarkdown}`,
|
|
@@ -819,24 +830,6 @@ export function realDataFinalGuard(
|
|
|
819
830
|
}
|
|
820
831
|
|
|
821
832
|
const configuredSources = configuredDataSourceLabels(context.toolResults);
|
|
822
|
-
if (firstPartySourceShouldBeTried) {
|
|
823
|
-
return {
|
|
824
|
-
retryMessage:
|
|
825
|
-
"The user asked for live analytics, and the built-in first-party Analytics source is available even though no external provider is connected. Call `query-agent-native-analytics` for first-party product, usage, conversion, or observability data and answer from that result. If the request specifically names an external provider, explain what is missing and include the real Connect data sources link.",
|
|
826
|
-
fallbackMessage:
|
|
827
|
-
"I couldn't complete a grounded first-party Analytics query yet. Please retry and I'll use the built-in Analytics source before asking you to connect an external provider.",
|
|
828
|
-
maxRetries: 2,
|
|
829
|
-
expandToolSurface: true,
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
if (noConnectedExternalSources) {
|
|
833
|
-
return {
|
|
834
|
-
retryMessage: `The user asked for live analytics, but data-source-status found no connected external providers. The built-in first-party source is still available for first-party Analytics data. If this request needs an external source, respond naturally in the context of the user's question, explain what is missing, and include ${setupMarkdown}. Do not use a generic canned no-data response.`,
|
|
835
|
-
fallbackMessage: `I can help with that once the relevant source is connected. ${setupMarkdown}`,
|
|
836
|
-
maxRetries: 2,
|
|
837
|
-
expandToolSurface: true,
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
833
|
const configuredSourceGuidance = configuredSources.length
|
|
841
834
|
? ` \`data-source-status\` already confirmed these connected sources: ${configuredSources.join(", ")}. Do not claim that no sources are connected and do not ask the user to reconnect them. Immediately call the relevant query action for one of those sources.`
|
|
842
835
|
: "";
|
package/dist/action.d.ts
CHANGED
|
@@ -398,6 +398,15 @@ interface DefineActionWithSchema<TSchema extends StandardSchemaV1, TReturn = any
|
|
|
398
398
|
* Only set this manually when you need to override the inference — e.g. a
|
|
399
399
|
* POST action that only reads data but can't use GET for a protocol reason. */
|
|
400
400
|
readOnly?: boolean;
|
|
401
|
+
/** Declares that a successful call returns real evidence retrieved from a data
|
|
402
|
+
* source at call time — a provider API, a warehouse, a connected repo, or the
|
|
403
|
+
* app's own event/observability store. Apps that police fabricated answers
|
|
404
|
+
* (see the Analytics final-response guard) read this instead of keeping a
|
|
405
|
+
* hand-maintained list of action names, which drifts the moment a new source
|
|
406
|
+
* action ships. Orthogonal to `readOnly`: a write that probes a live endpoint
|
|
407
|
+
* and returns the outcome is still grounding. Metadata-only reads (schema,
|
|
408
|
+
* catalogs, saved config) are not. */
|
|
409
|
+
grounding?: boolean;
|
|
401
410
|
/** Set false for read-only tools that should stay available in Act mode but
|
|
402
411
|
* must not run during Plan mode because they perform substantive work
|
|
403
412
|
* rather than lightweight inspection. Defaults to allowed when read-only. */
|
|
@@ -538,6 +547,9 @@ interface DefineActionWithParams<TParams extends Record<string, ParameterSchema>
|
|
|
538
547
|
/** If true, the framework will NOT emit a screen-refresh change event after a
|
|
539
548
|
* successful call. Auto-inferred as `true` when `http.method === "GET"`. */
|
|
540
549
|
readOnly?: boolean;
|
|
550
|
+
/** Declares that a successful call returns real evidence retrieved from a data
|
|
551
|
+
* source at call time. See the schema overload above. */
|
|
552
|
+
grounding?: boolean;
|
|
541
553
|
/** Set false for read-only tools that should stay available in Act mode but
|
|
542
554
|
* must not run during Plan mode. See the schema overload above. */
|
|
543
555
|
allowInPlanMode?: boolean;
|
|
@@ -602,6 +614,7 @@ export interface ActionDefinition<TInput, TReturn> {
|
|
|
602
614
|
readonly maxBodyBytes?: number;
|
|
603
615
|
readonly agentTool?: boolean;
|
|
604
616
|
readonly readOnly?: boolean;
|
|
617
|
+
readonly grounding?: boolean;
|
|
605
618
|
readonly allowInPlanMode?: boolean;
|
|
606
619
|
readonly planMode?: ActionPlanModeConfig<TInput>;
|
|
607
620
|
readonly parallelSafe?: boolean;
|