Skip to content

Commit aa6a3e1

Browse files
committed
Keep connection list health compact by default
Fixes #1647
1 parent f08e84d commit aa6a3e1

4 files changed

Lines changed: 82 additions & 9 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@executor-js/sdk": patch
3+
---
4+
5+
Keep `connections.list` health output compact unless callers opt into diagnostics with `verbose: true`. Default list responses now retain only the health status, identity, and check timestamp; verbose responses continue to include HTTP status, diagnostic detail, and bounded upstream response samples.

packages/core/sdk/src/connections.test.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "@effect/vitest";
2-
import { Effect, Predicate, Result } from "effect";
2+
import { Effect, Predicate, Result, Schema } from "effect";
33

44
import {
55
AuthTemplateSlug,
@@ -11,6 +11,7 @@ import {
1111
ToolName,
1212
} from "./ids";
1313
import { createExecutor } from "./executor";
14+
import { HealthCheckResult } from "./health-check";
1415
import { definePlugin } from "./plugin";
1516
import type { CredentialProvider } from "./provider";
1617
import { makeTestConfig, makeTestExecutor } from "./testing";
@@ -43,6 +44,11 @@ const memoryProvider = (): CredentialProvider => {
4344
const INTEG = IntegrationSlug.make("vercel");
4445
const TEMPLATE = AuthTemplateSlug.make("apiKey");
4546

47+
const ConnectionListHealthOutput = Schema.Struct({
48+
connections: Schema.Array(Schema.Struct({ lastHealth: Schema.NullOr(HealthCheckResult) })),
49+
});
50+
const decodeConnectionListHealthOutput = Schema.decodeUnknownEffect(ConnectionListHealthOutput);
51+
4652
const demoPlugin = definePlugin(() => ({
4753
id: "demo" as const,
4854
credentialProviders: [memoryProvider()],
@@ -263,6 +269,58 @@ describe("connections.create", () => {
263269
});
264270

265271
describe("connections.list / get", () => {
272+
it.effect("only includes full health diagnostics in verbose core tool output", () =>
273+
Effect.gen(function* () {
274+
const config = makeTestConfig({ plugins: [demoPlugin] as const, coreTools: {} });
275+
const executor = yield* createExecutor(config);
276+
yield* executor.demo.seed();
277+
yield* executor.connections.create({
278+
owner: "org",
279+
name: ConnectionName.make("health"),
280+
integration: INTEG,
281+
template: TEMPLATE,
282+
value: "v",
283+
});
284+
285+
const health = {
286+
status: "healthy" as const,
287+
identity: "account@example.com",
288+
checkedAt: 1234,
289+
httpStatus: 200,
290+
detail: "GET /me returned 200",
291+
responseSample: [{ path: "user.email", value: "account@example.com" }],
292+
};
293+
yield* Effect.promise(() =>
294+
config.db.updateMany("connection", {
295+
where: (b) => b.and(b("integration", "=", String(INTEG)), b("name", "=", "health")),
296+
set: { last_health: health },
297+
}),
298+
);
299+
300+
const list = (input: { readonly verbose?: boolean }) =>
301+
executor
302+
.execute(ToolAddress.make("executor.coreTools.connections.list"), {
303+
integration: String(INTEG),
304+
owner: "org",
305+
...input,
306+
})
307+
.pipe(Effect.flatMap(decodeConnectionListHealthOutput));
308+
309+
const defaultList = yield* list({});
310+
const nonVerboseList = yield* list({ verbose: false });
311+
const verboseList = yield* list({ verbose: true });
312+
const summary = {
313+
status: "healthy",
314+
identity: "account@example.com",
315+
checkedAt: 1234,
316+
};
317+
318+
expect(defaultList.connections[0]?.lastHealth).toEqual(summary);
319+
expect(nonVerboseList.connections[0]?.lastHealth).toEqual(summary);
320+
expect(verboseList.connections[0]?.lastHealth).toEqual(health);
321+
}),
322+
);
323+
266324
it.effect("lists created connections and filters by integration", () =>
267325
Effect.gen(function* () {
268326
const executor = yield* setup();

packages/core/sdk/src/core-tools.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ const ConnectionsListInput = Schema.Struct({
8888
verbose: Schema.optional(Schema.Boolean),
8989
});
9090

91-
/** Lean per-connection shape for list scans. Omits the full `oauthScope`
92-
* grant string (a single connection's scope list can run to thousands of
93-
* characters and dominates the payload) in favor of `oauthScopeCount`. The
94-
* full scope is included only when the caller passes `verbose: true`. */
91+
/** Lean per-connection shape for list scans. The default projection summarizes
92+
* the full `oauthScope` grant string as `oauthScopeCount` and trims health
93+
* probe diagnostics. Those optional fields are populated only for `verbose:
94+
* true`. */
9595
const ConnectionListItem = Schema.Struct({
9696
owner: OwnerSchema,
9797
name: Schema.String,
@@ -389,8 +389,8 @@ const connectionToOutput = (connection: Connection) => ({
389389
const oauthScopeCount = (scope: string | null | undefined): number | null =>
390390
scope == null ? null : scope.split(/\s+/).filter(Boolean).length;
391391

392-
/** Lean projection for `connections.list`. Summarizes `oauthScope` to a count
393-
* unless `verbose`, where the full grant string is included too. */
392+
/** Lean projection for `connections.list`. Summarizes `oauthScope` and health
393+
* diagnostics unless `verbose`, where the full grant string is included too. */
394394
const connectionToListItem = (connection: Connection, verbose: boolean) => ({
395395
owner: connection.owner,
396396
name: String(connection.name),
@@ -404,7 +404,17 @@ const connectionToListItem = (connection: Connection, verbose: boolean) => ({
404404
oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient),
405405
oauthClientOwner: connection.oauthClientOwner ?? null,
406406
oauthScopeCount: oauthScopeCount(connection.oauthScope),
407-
lastHealth: connection.lastHealth ?? null,
407+
// Keep full probe diagnostics behind the explicit verbose opt-in.
408+
lastHealth:
409+
connection.lastHealth == null || verbose
410+
? (connection.lastHealth ?? null)
411+
: {
412+
status: connection.lastHealth.status,
413+
...(connection.lastHealth.identity !== undefined
414+
? { identity: connection.lastHealth.identity }
415+
: {}),
416+
checkedAt: connection.lastHealth.checkedAt,
417+
},
408418
...(verbose ? { oauthScope: connection.oauthScope ?? null } : {}),
409419
});
410420

packages/core/sdk/src/executor.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ describe("createExecutor", () => {
438438

439439
const listed = yield* executor.execute(
440440
ToolAddress.make("executor.coreTools.connections.list"),
441-
{ integration: "diagnostics" },
441+
{ integration: "diagnostics", verbose: true },
442442
);
443443
expect(listed).toMatchObject({
444444
connections: [

0 commit comments

Comments
 (0)