@integrity-labs/agt-cli 0.28.777 → 0.28.779
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/dist/bin/agt.js +5 -5
- package/dist/{chunk-VACVLMSQ.js → chunk-3IDAWL35.js} +4 -4
- package/dist/{chunk-N6DU2RYO.js → chunk-LLBJLKGL.js} +4 -1
- package/dist/{chunk-N6DU2RYO.js.map → chunk-LLBJLKGL.js.map} +1 -1
- package/dist/{chunk-NRMBKDER.js → chunk-NTWABPKO.js} +2 -2
- package/dist/{claude-pair-runtime-MI65DVTR.js → claude-pair-runtime-3J7KK6BP.js} +2 -2
- package/dist/lib/manager-worker.js +13 -13
- package/dist/mcp/augmented-admin.js +7 -1
- package/dist/mcp/direct-chat-channel.js +453 -0
- package/dist/mcp/index.js +453 -0
- package/dist/mcp/origami.js +453 -0
- package/dist/mcp/slack-channel.js +453 -0
- package/dist/mcp/telegram-channel.js +453 -0
- package/dist/{persistent-session-CGIB77UQ.js → persistent-session-GHIBAWHI.js} +3 -3
- package/dist/{responsiveness-probe-YINXZH3S.js → responsiveness-probe-TT3LAALK.js} +3 -3
- package/dist/{session-auth-dead-OZBNHFPN.js → session-auth-dead-OHPGSHYL.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-VACVLMSQ.js.map → chunk-3IDAWL35.js.map} +0 -0
- /package/dist/{chunk-NRMBKDER.js.map → chunk-NTWABPKO.js.map} +0 -0
- /package/dist/{claude-pair-runtime-MI65DVTR.js.map → claude-pair-runtime-3J7KK6BP.js.map} +0 -0
- /package/dist/{persistent-session-CGIB77UQ.js.map → persistent-session-GHIBAWHI.js.map} +0 -0
- /package/dist/{responsiveness-probe-YINXZH3S.js.map → responsiveness-probe-TT3LAALK.js.map} +0 -0
- /package/dist/{session-auth-dead-OZBNHFPN.js.map → session-auth-dead-OHPGSHYL.js.map} +0 -0
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
rotateDailySession,
|
|
19
19
|
sessionFileExists,
|
|
20
20
|
todayLocalIso
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-LLBJLKGL.js";
|
|
22
22
|
import {
|
|
23
23
|
reapOrphanChannelMcps
|
|
24
24
|
} from "./chunk-XWVM4KPK.js";
|
|
@@ -5141,4 +5141,4 @@ export {
|
|
|
5141
5141
|
stopAllSessionsAndWait,
|
|
5142
5142
|
getProjectDir
|
|
5143
5143
|
};
|
|
5144
|
-
//# sourceMappingURL=chunk-
|
|
5144
|
+
//# sourceMappingURL=chunk-NTWABPKO.js.map
|
|
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
|
|
|
100
100
|
return { ok: true };
|
|
101
101
|
} catch {
|
|
102
102
|
}
|
|
103
|
-
const { resolveClaudeBinary } = await import("./persistent-session-
|
|
103
|
+
const { resolveClaudeBinary } = await import("./persistent-session-GHIBAWHI.js");
|
|
104
104
|
const claudeBin = resolveClaudeBinary();
|
|
105
105
|
const pairEnv = {
|
|
106
106
|
...process.env,
|
|
@@ -373,4 +373,4 @@ export {
|
|
|
373
373
|
startClaudePair,
|
|
374
374
|
submitClaudePairCode
|
|
375
375
|
};
|
|
376
|
-
//# sourceMappingURL=claude-pair-runtime-
|
|
376
|
+
//# sourceMappingURL=claude-pair-runtime-3J7KK6BP.js.map
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
safeWriteJsonAtomic,
|
|
59
59
|
setConfigHash,
|
|
60
60
|
tripClass
|
|
61
|
-
} from "../chunk-
|
|
61
|
+
} from "../chunk-3IDAWL35.js";
|
|
62
62
|
import {
|
|
63
63
|
getProjectDir as getProjectDir2,
|
|
64
64
|
getReadyTasks,
|
|
@@ -120,7 +120,7 @@ import {
|
|
|
120
120
|
takeZombieDetection,
|
|
121
121
|
toOpencodeModel,
|
|
122
122
|
writeEgressAllowlist
|
|
123
|
-
} from "../chunk-
|
|
123
|
+
} from "../chunk-NTWABPKO.js";
|
|
124
124
|
import {
|
|
125
125
|
ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
|
|
126
126
|
AnchorSessionClient,
|
|
@@ -209,7 +209,7 @@ import {
|
|
|
209
209
|
subagentActivityAgeSeconds,
|
|
210
210
|
sumTranscriptUsageInWindow,
|
|
211
211
|
transcriptActivityAgeSeconds
|
|
212
|
-
} from "../chunk-
|
|
212
|
+
} from "../chunk-LLBJLKGL.js";
|
|
213
213
|
import {
|
|
214
214
|
reapOrphanChannelMcps
|
|
215
215
|
} from "../chunk-XWVM4KPK.js";
|
|
@@ -12870,7 +12870,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
|
|
|
12870
12870
|
var dayRolloverInboundHold = /* @__PURE__ */ new Map();
|
|
12871
12871
|
var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
|
|
12872
12872
|
async function channelInboundActivityAgeSecondsFor(codeName) {
|
|
12873
|
-
const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-
|
|
12873
|
+
const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-TT3LAALK.js");
|
|
12874
12874
|
const newest = newestPendingInboundActivityMtimeMs(dirname10(paneLogPath(codeName)));
|
|
12875
12875
|
if (newest === null) return null;
|
|
12876
12876
|
return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
|
|
@@ -13530,7 +13530,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
13530
13530
|
var lastVersionCheckAt = 0;
|
|
13531
13531
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
13532
13532
|
var lastResponsivenessProbeAt = 0;
|
|
13533
|
-
var agtCliVersion = true ? "0.28.
|
|
13533
|
+
var agtCliVersion = true ? "0.28.779" : "dev";
|
|
13534
13534
|
function resolveBrewPath(execFileSync2) {
|
|
13535
13535
|
try {
|
|
13536
13536
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -15074,7 +15074,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
15074
15074
|
if (codeNames.length === 0) return;
|
|
15075
15075
|
void (async () => {
|
|
15076
15076
|
try {
|
|
15077
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
15077
|
+
const { collectDiagnostics } = await import("../persistent-session-GHIBAWHI.js");
|
|
15078
15078
|
await api.post("/host/heartbeat", {
|
|
15079
15079
|
host_id: hostId,
|
|
15080
15080
|
agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
|
|
@@ -15215,7 +15215,7 @@ async function pollCycleInner() {
|
|
|
15215
15215
|
}
|
|
15216
15216
|
try {
|
|
15217
15217
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
15218
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
15218
|
+
const { collectDiagnostics } = await import("../persistent-session-GHIBAWHI.js");
|
|
15219
15219
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
15220
15220
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
|
|
15221
15221
|
let tailscaleHostname;
|
|
@@ -15384,7 +15384,7 @@ async function pollCycleInner() {
|
|
|
15384
15384
|
collectPanelessActivityProbes,
|
|
15385
15385
|
getResponsivenessIntervalMs,
|
|
15386
15386
|
occupancyQualificationClassifications
|
|
15387
|
-
} = await import("../responsiveness-probe-
|
|
15387
|
+
} = await import("../responsiveness-probe-TT3LAALK.js");
|
|
15388
15388
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
15389
15389
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
15390
15390
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -15494,7 +15494,7 @@ async function pollCycleInner() {
|
|
|
15494
15494
|
collectResponsivenessProbes,
|
|
15495
15495
|
livePendingInboundOldestAgeSeconds,
|
|
15496
15496
|
parkPendingInbound
|
|
15497
|
-
} = await import("../responsiveness-probe-
|
|
15497
|
+
} = await import("../responsiveness-probe-TT3LAALK.js");
|
|
15498
15498
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
|
|
15499
15499
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
15500
15500
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -15650,7 +15650,7 @@ async function pollCycleInner() {
|
|
|
15650
15650
|
}
|
|
15651
15651
|
try {
|
|
15652
15652
|
const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
|
|
15653
|
-
const { probeSessionAuth } = await import("../session-auth-dead-
|
|
15653
|
+
const { probeSessionAuth } = await import("../session-auth-dead-OHPGSHYL.js");
|
|
15654
15654
|
const observations = [];
|
|
15655
15655
|
const pendingCacheCommits = [];
|
|
15656
15656
|
const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
|
|
@@ -19690,7 +19690,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
19690
19690
|
void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
|
|
19691
19691
|
void (async () => {
|
|
19692
19692
|
try {
|
|
19693
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
19693
|
+
const { collectDiagnostics } = await import("../persistent-session-GHIBAWHI.js");
|
|
19694
19694
|
await api.post("/host/heartbeat", {
|
|
19695
19695
|
host_id: hostId,
|
|
19696
19696
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
|
|
@@ -19736,7 +19736,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
19736
19736
|
}
|
|
19737
19737
|
try {
|
|
19738
19738
|
const hostId = await getHostId();
|
|
19739
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
19739
|
+
const { collectDiagnostics } = await import("../persistent-session-GHIBAWHI.js");
|
|
19740
19740
|
await api.post("/host/heartbeat", {
|
|
19741
19741
|
host_id: hostId,
|
|
19742
19742
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
|
|
@@ -20372,7 +20372,7 @@ async function processClaudePairSessions(agents) {
|
|
|
20372
20372
|
killPairSession,
|
|
20373
20373
|
pairTmuxSession,
|
|
20374
20374
|
finalizeClaudePairOnboarding
|
|
20375
|
-
} = await import("../claude-pair-runtime-
|
|
20375
|
+
} = await import("../claude-pair-runtime-3J7KK6BP.js");
|
|
20376
20376
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
20377
20377
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
20378
20378
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -21477,6 +21477,12 @@ var resumeAgentSchema = strictObject({
|
|
|
21477
21477
|
});
|
|
21478
21478
|
var listAlertsSchema = strictObject({
|
|
21479
21479
|
severity: external_exports.string().max(16).optional().describe("Filter by severity (critical | warning | info)."),
|
|
21480
|
+
// ENG-8566: without this, an old alert of a specific kind is unreachable —
|
|
21481
|
+
// the listing is newest-first and capped at 100, and the open board exceeds
|
|
21482
|
+
// that. Twelve slack_manifest_drift warnings sat unfindable for 48 days.
|
|
21483
|
+
kind: external_exports.string().max(64).optional().describe(
|
|
21484
|
+
"Filter by alert kind, exact match (e.g. slack_manifest_drift, host_down, integration_down). Use this to reach older alerts of one kind: the list is newest-first and capped at 100, so without a kind filter an old alert below that cutoff cannot be retrieved at all."
|
|
21485
|
+
),
|
|
21480
21486
|
open: external_exports.boolean().optional().describe("When true, only currently-open (unclosed) alerts."),
|
|
21481
21487
|
limit: limitSchema
|
|
21482
21488
|
});
|
|
@@ -21686,7 +21692,7 @@ server.registerTool(
|
|
|
21686
21692
|
);
|
|
21687
21693
|
server.registerTool(
|
|
21688
21694
|
"debug_list_alerts",
|
|
21689
|
-
{ description: "List recent platform alerts across authorized orgs (host-down, agent-stale, probe-timeout, auth-failed), including NULL-team host alerts. Returns a projection per alert (kind, severity, message, source, open/closed state). Filter with { severity, open, limit }.", inputSchema: listAlertsSchema },
|
|
21695
|
+
{ description: "List recent platform alerts across authorized orgs (host-down, agent-stale, probe-timeout, auth-failed), including NULL-team host alerts. Returns a projection per alert (kind, severity, message, source, open/closed state), plus { count, total, truncated } \u2014 `total` is the matches before the 100-row cap, so `truncated: true` means you are NOT seeing everything. Filter with { kind, severity, open, limit }. **Use `kind` to find older alerts**: results are newest-first and hard-capped at 100, so on a busy board an alert below that cutoff is unreachable without it (ENG-8566: 12 slack_manifest_drift warnings sat unfindable for 48 days for exactly this reason).", inputSchema: listAlertsSchema },
|
|
21690
21696
|
async (args) => {
|
|
21691
21697
|
try {
|
|
21692
21698
|
const result = await client.listAlerts(args);
|
|
@@ -32883,6 +32883,459 @@ var ASSET_TYPES = {
|
|
|
32883
32883
|
var DEFAULT_CHUNK_BUDGET_BYTES = 180 * 1024;
|
|
32884
32884
|
var textEncoder = new TextEncoder();
|
|
32885
32885
|
|
|
32886
|
+
// ../core/dist/integrations/oauth-providers.js
|
|
32887
|
+
var OAUTH_PROVIDERS = {
|
|
32888
|
+
"google-workspace": {
|
|
32889
|
+
definitionId: "google-workspace",
|
|
32890
|
+
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
32891
|
+
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
32892
|
+
revokeUrl: "https://oauth2.googleapis.com/revoke",
|
|
32893
|
+
defaultScopes: [
|
|
32894
|
+
"https://www.googleapis.com/auth/gmail.modify",
|
|
32895
|
+
"https://www.googleapis.com/auth/calendar",
|
|
32896
|
+
"https://www.googleapis.com/auth/drive",
|
|
32897
|
+
"https://www.googleapis.com/auth/spreadsheets",
|
|
32898
|
+
"https://www.googleapis.com/auth/documents",
|
|
32899
|
+
"https://www.googleapis.com/auth/chat.messages",
|
|
32900
|
+
"https://www.googleapis.com/auth/chat.spaces.readonly"
|
|
32901
|
+
],
|
|
32902
|
+
supportsRefresh: true,
|
|
32903
|
+
extraAuthorizeParams: {
|
|
32904
|
+
access_type: "offline",
|
|
32905
|
+
prompt: "consent"
|
|
32906
|
+
},
|
|
32907
|
+
clientAuthMethod: "body",
|
|
32908
|
+
userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo"
|
|
32909
|
+
},
|
|
32910
|
+
"github": {
|
|
32911
|
+
definitionId: "github",
|
|
32912
|
+
authorizeUrl: "https://github.com/login/oauth/authorize",
|
|
32913
|
+
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
32914
|
+
// ENG-6187: revoke the existing grant on reconnect so GitHub re-prompts and
|
|
32915
|
+
// the four scopes below are actually granted. Without this, a stale narrow
|
|
32916
|
+
// grant (e.g. an old read:user-only authorization) is silently re-issued and
|
|
32917
|
+
// the missing-scopes banner loops forever.
|
|
32918
|
+
grantRevokeUrl: "https://api.github.com/applications/{client_id}/grant",
|
|
32919
|
+
defaultScopes: ["repo", "read:org", "gist", "workflow"],
|
|
32920
|
+
supportsRefresh: true,
|
|
32921
|
+
extraAuthorizeParams: {},
|
|
32922
|
+
clientAuthMethod: "body",
|
|
32923
|
+
userInfoUrl: "https://api.github.com/user"
|
|
32924
|
+
},
|
|
32925
|
+
"granola": {
|
|
32926
|
+
// Granola MCP — remote streamable-HTTP at https://mcp.granola.ai/mcp.
|
|
32927
|
+
// The AS is at mcp-auth.granola.ai and exposes RFC 8414 metadata at
|
|
32928
|
+
// /.well-known/oauth-authorization-server. Auth is OAuth 2.0 with
|
|
32929
|
+
// mandatory PKCE (S256) and a public client (no client_secret) issued
|
|
32930
|
+
// via Dynamic Client Registration (RFC 7591). The bootstrap script
|
|
32931
|
+
// (`packages/api/scripts/dcr-register.ts`) registers a client once at
|
|
32932
|
+
// deploy time; OAUTH_GRANOLA_CLIENT_ID is set from its output.
|
|
32933
|
+
definitionId: "granola",
|
|
32934
|
+
authorizeUrl: "https://mcp-auth.granola.ai/oauth2/authorize",
|
|
32935
|
+
tokenUrl: "https://mcp-auth.granola.ai/oauth2/token",
|
|
32936
|
+
// Minimal scope set: `offline_access` earns the refresh_token so the
|
|
32937
|
+
// refresh cron can rotate the bearer without operator action; `openid`
|
|
32938
|
+
// is required for the OIDC code flow even when we don't request an
|
|
32939
|
+
// id_token. Profile/email are intentionally omitted — we have no
|
|
32940
|
+
// userInfoUrl wired up here, so requesting them would over-ask consent
|
|
32941
|
+
// for fields the callback can't read.
|
|
32942
|
+
defaultScopes: ["openid", "offline_access"],
|
|
32943
|
+
supportsRefresh: true,
|
|
32944
|
+
extraAuthorizeParams: {},
|
|
32945
|
+
clientAuthMethod: "body",
|
|
32946
|
+
pkce: "S256",
|
|
32947
|
+
publicClient: true,
|
|
32948
|
+
mcpUrl: "https://mcp.granola.ai/mcp",
|
|
32949
|
+
// Curated surface (matches the catalog seed's defined_scopes[].tools).
|
|
32950
|
+
toolAllowlist: ["search-meetings", "read-transcript", "read-summary", "list-folders"]
|
|
32951
|
+
},
|
|
32952
|
+
"brand-ninja": {
|
|
32953
|
+
// ENG-6820: Brand Ninja External-Content MCP, remote streamable-HTTP at
|
|
32954
|
+
// https://ext-api.app.brandninja.ai/v1/mcp. Same shape as Granola: the
|
|
32955
|
+
// server exposes RFC 8414 authorization-server metadata at
|
|
32956
|
+
// /.well-known/oauth-authorization-server (values below are taken verbatim
|
|
32957
|
+
// from that document, not inferred). Auth is OAuth 2.0 authorization-code
|
|
32958
|
+
// with mandatory PKCE (S256) and a public client (token_endpoint_auth_method
|
|
32959
|
+
// 'none') issued via Dynamic Client Registration (RFC 7591). The bootstrap
|
|
32960
|
+
// script (packages/api/scripts/dcr-register.ts) registers a client once at
|
|
32961
|
+
// deploy time against the registration_endpoint
|
|
32962
|
+
// (https://ext-api.app.brandninja.ai/v1/oauth/register); OAUTH_BRAND_NINJA_CLIENT_ID
|
|
32963
|
+
// is set from its output. The AS advertises the refresh_token grant, so the
|
|
32964
|
+
// shared oauth-refresh cron rotates the bearer without operator action.
|
|
32965
|
+
definitionId: "brand-ninja",
|
|
32966
|
+
authorizeUrl: "https://prod-brandninja.auth.ap-southeast-2.amazoncognito.com/oauth2/authorize",
|
|
32967
|
+
tokenUrl: "https://prod-brandninja.auth.ap-southeast-2.amazoncognito.com/oauth2/token",
|
|
32968
|
+
// The resource server (ext-api.app.brandninja.ai) advertises exactly two
|
|
32969
|
+
// scopes: external-api/content.write (the default content surface) and
|
|
32970
|
+
// external-api/admin (read-only credential metadata, granted per account
|
|
32971
|
+
// admin). Default install is least-privilege: content.write only; an
|
|
32972
|
+
// operator can widen to admin out of band. No openid/offline_access in the
|
|
32973
|
+
// advertised scope set, so Cognito issues the refresh_token for the code grant
|
|
32974
|
+
// regardless, so requesting only the resource scope keeps consent minimal.
|
|
32975
|
+
defaultScopes: ["external-api/content.write"],
|
|
32976
|
+
supportsRefresh: true,
|
|
32977
|
+
extraAuthorizeParams: {},
|
|
32978
|
+
clientAuthMethod: "body",
|
|
32979
|
+
pkce: "S256",
|
|
32980
|
+
publicClient: true,
|
|
32981
|
+
mcpUrl: "https://ext-api.app.brandninja.ai/v1/mcp",
|
|
32982
|
+
// Curated surface (matches the catalog seed's defined_scopes[].tools).
|
|
32983
|
+
toolAllowlist: [
|
|
32984
|
+
"submit_content_request",
|
|
32985
|
+
"get_content_status",
|
|
32986
|
+
"list_content_requests",
|
|
32987
|
+
"list_channels",
|
|
32988
|
+
"list_brands",
|
|
32989
|
+
"list_topics",
|
|
32990
|
+
"list_timeline_templates",
|
|
32991
|
+
"list_content_types",
|
|
32992
|
+
"list_source_skills",
|
|
32993
|
+
"list_output_skills",
|
|
32994
|
+
"list_conversion_skills",
|
|
32995
|
+
"list_credentials",
|
|
32996
|
+
"search_transcripts",
|
|
32997
|
+
"create_source_clips",
|
|
32998
|
+
"create_timeline_ranking",
|
|
32999
|
+
"create_topic",
|
|
33000
|
+
"create_timeline_template",
|
|
33001
|
+
"create_knowledge",
|
|
33002
|
+
"link_knowledge"
|
|
33003
|
+
]
|
|
33004
|
+
},
|
|
33005
|
+
"kajabi": {
|
|
33006
|
+
// Kajabi MCP — remote streamable-HTTP at https://mcp.kajabi.com/mcp.
|
|
33007
|
+
// Same Granola/Brand-Ninja shape: OAuth 2.0 authorization-code with
|
|
33008
|
+
// mandatory PKCE (S256) and a public client (token_endpoint_auth_method
|
|
33009
|
+
// 'none') issued via Dynamic Client Registration (RFC 7591). Values below
|
|
33010
|
+
// are taken verbatim from Kajabi's RFC 8414 metadata at
|
|
33011
|
+
// https://mcp.kajabi.com/.well-known/oauth-authorization-server (a Rails
|
|
33012
|
+
// Doorkeeper AS), not inferred. NOTE the authorize host differs from the
|
|
33013
|
+
// token host: authorize is on app.kajabi.com (the login surface), while
|
|
33014
|
+
// token/register/revoke are on mcp.kajabi.com — do NOT "normalise" them to
|
|
33015
|
+
// one host. The bootstrap script (packages/api/scripts/dcr-register.ts)
|
|
33016
|
+
// registers a client once at deploy time against
|
|
33017
|
+
// https://mcp.kajabi.com/mcp/oauth/register; OAUTH_KAJABI_CLIENT_ID is set
|
|
33018
|
+
// from its output. Doorkeeper RESTRICTS a dynamic client to the scopes it
|
|
33019
|
+
// registered with, so register with at least the union of defaultScopes
|
|
33020
|
+
// below (--scope 'read write:contacts write:emails write:content
|
|
33021
|
+
// write:commerce'). Widening defaultScopes forces a client re-register
|
|
33022
|
+
// (new client_id) AND a per-connection re-consent: existing tokens keep
|
|
33023
|
+
// the old scope set and their refreshes fail under the new client_id, so
|
|
33024
|
+
// each connection flips to needs_reauth until the user re-runs Connect
|
|
33025
|
+
// (ENG-7483). The AS advertises the refresh_token grant (no
|
|
33026
|
+
// openid/offline_access scope needed), so the shared oauth-refresh cron
|
|
33027
|
+
// rotates the bearer without operator action.
|
|
33028
|
+
definitionId: "kajabi",
|
|
33029
|
+
authorizeUrl: "https://app.kajabi.com/mcp/oauth/authorize",
|
|
33030
|
+
tokenUrl: "https://mcp.kajabi.com/mcp/oauth/token",
|
|
33031
|
+
revokeUrl: "https://mcp.kajabi.com/mcp/oauth/revoke",
|
|
33032
|
+
// Coarse Doorkeeper scopes (NOT openid-style). Cross-domain reads (`read`)
|
|
33033
|
+
// plus the write surfaces this integration ships: contact tags/segments,
|
|
33034
|
+
// email broadcasts/sequences, and course updates (write:content gates
|
|
33035
|
+
// update_course, the course-thumbnail path, ENG-7483). write:commerce is
|
|
33036
|
+
// requested now (Brad's call on ENG-7483) so a later offer/pricing-write
|
|
33037
|
+
// enablement needs no extra re-consent round; the tool allowlist below
|
|
33038
|
+
// still exposes no commerce write, so the callable surface stays minimal.
|
|
33039
|
+
defaultScopes: ["read", "write:contacts", "write:emails", "write:content", "write:commerce"],
|
|
33040
|
+
supportsRefresh: true,
|
|
33041
|
+
extraAuthorizeParams: {},
|
|
33042
|
+
clientAuthMethod: "body",
|
|
33043
|
+
pkce: "S256",
|
|
33044
|
+
publicClient: true,
|
|
33045
|
+
mcpUrl: "https://mcp.kajabi.com/mcp",
|
|
33046
|
+
// Curated surface (matches the catalog seed's defined_scopes[].tools). Kajabi's
|
|
33047
|
+
// live MCP advertises ~111 tools; the agent only needs these 28.
|
|
33048
|
+
// CS-1427: `enable_toolset` is included so an agent can activate its own
|
|
33049
|
+
// products/commerce + analytics toolsets on the connection (Kajabi gates those
|
|
33050
|
+
// tool groups behind a runtime toolset that returns "not active" until enabled).
|
|
33051
|
+
// Low-risk: this proxy still caps every tools/call to the allowlist, so a broad
|
|
33052
|
+
// activation can't widen what's actually callable.
|
|
33053
|
+
// ENG-7483: `get_course`/`update_course` enable the course-thumbnail refresh
|
|
33054
|
+
// (update_course covers title/description/thumbnail per Kajabi's MCP docs).
|
|
33055
|
+
// Fail-safe on naming drift: an allowlisted tool the server doesn't advertise
|
|
33056
|
+
// is logged and skipped by the proxy, never a break.
|
|
33057
|
+
toolAllowlist: [
|
|
33058
|
+
"list_sites",
|
|
33059
|
+
"get_site_summary",
|
|
33060
|
+
"select_site",
|
|
33061
|
+
"search_contacts",
|
|
33062
|
+
"get_contact",
|
|
33063
|
+
"search_products",
|
|
33064
|
+
"get_product",
|
|
33065
|
+
"list_offers",
|
|
33066
|
+
"get_offer",
|
|
33067
|
+
"list_offer_purchases",
|
|
33068
|
+
"get_revenue_analytics",
|
|
33069
|
+
"get_contacts_analytics",
|
|
33070
|
+
"list_tags",
|
|
33071
|
+
"create_tag",
|
|
33072
|
+
"tag_contact",
|
|
33073
|
+
"untag_contact",
|
|
33074
|
+
"list_segments",
|
|
33075
|
+
"create_segment",
|
|
33076
|
+
"update_segment",
|
|
33077
|
+
"list_broadcasts",
|
|
33078
|
+
"get_broadcast",
|
|
33079
|
+
"create_broadcast",
|
|
33080
|
+
"list_sequences",
|
|
33081
|
+
"get_sequence",
|
|
33082
|
+
"create_sequence",
|
|
33083
|
+
"enable_toolset",
|
|
33084
|
+
"get_course",
|
|
33085
|
+
"update_course",
|
|
33086
|
+
// ENG-7629 follow-up: pages (Dee's landing-page edit) + themes (CS-1448
|
|
33087
|
+
// theme-builder broadcast content). Callable via the proxy union manifest
|
|
33088
|
+
// + per-call enable; matches the new kajabi:pages catalog scope.
|
|
33089
|
+
"list_landing_pages",
|
|
33090
|
+
"get_landing_page",
|
|
33091
|
+
"update_landing_page",
|
|
33092
|
+
"get_theme_content"
|
|
33093
|
+
],
|
|
33094
|
+
// CS-1446: Kajabi gates several tool groups behind a runtime `enable_toolset`
|
|
33095
|
+
// and does not advertise their tools in `tools/list` until the group is
|
|
33096
|
+
// active. The harness freezes its callable-tool manifest from the
|
|
33097
|
+
// connect-time `tools/list`, so a group the agent activates later never
|
|
33098
|
+
// becomes callable (repro: enable_toolset('courses') → get_course/
|
|
33099
|
+
// update_course still "No such tool available"). Pre-activate the gated
|
|
33100
|
+
// groups whose tools we allowlist so they are advertised in that first
|
|
33101
|
+
// manifest. Names are Kajabi's toolset ids (courses per CS-1446;
|
|
33102
|
+
// products/commerce/analytics per CS-1427). Best-effort + idempotent: an
|
|
33103
|
+
// unknown/unscoped name is logged and skipped by the proxy, and the
|
|
33104
|
+
// toolAllowlist above still caps what is actually callable.
|
|
33105
|
+
//
|
|
33106
|
+
// ENG-8392: `contacts` + `emails` were missing, so thirteen allowlisted
|
|
33107
|
+
// tools were unreachable by construction — list_tags, create_tag,
|
|
33108
|
+
// tag_contact, untag_contact, list_segments, create_segment,
|
|
33109
|
+
// update_segment (contacts) and list_broadcasts, get_broadcast,
|
|
33110
|
+
// create_broadcast, list_sequences, get_sequence, create_sequence
|
|
33111
|
+
// (emails). A customer agent was blocked on exactly those.
|
|
33112
|
+
//
|
|
33113
|
+
// These two ids are Kajabi's OWN spelling, not ours: the server names the
|
|
33114
|
+
// toolset in its rejection, e.g. `list_tags` returns
|
|
33115
|
+
// "The 'contacts' toolset is not active. Call enable_toolset with
|
|
33116
|
+
// name: 'contacts' to activate it first."
|
|
33117
|
+
// and `list_sequences` returns the same with 'emails'. Sourced that way
|
|
33118
|
+
// deliberately — a wrong id here is logged and SKIPPED by the proxy, so a
|
|
33119
|
+
// guess would look applied and change nothing.
|
|
33120
|
+
//
|
|
33121
|
+
// Why this went unnoticed for four rounds: `get_contacts_analytics` lives
|
|
33122
|
+
// in the `analytics` group, which we already pre-enable. So the contacts
|
|
33123
|
+
// family read as partly alive (you could see a site's contact count and
|
|
33124
|
+
// growth curve) while every tag and segment call failed.
|
|
33125
|
+
preEnableToolsets: [
|
|
33126
|
+
"courses",
|
|
33127
|
+
"products",
|
|
33128
|
+
"commerce",
|
|
33129
|
+
"analytics",
|
|
33130
|
+
"pages",
|
|
33131
|
+
"themes",
|
|
33132
|
+
"contacts",
|
|
33133
|
+
"emails"
|
|
33134
|
+
],
|
|
33135
|
+
// ENG-8512: `update_course.thumbnail` stores a RELATIVE path into Kajabi's
|
|
33136
|
+
// own storage — it is rendered as
|
|
33137
|
+
// https://kajabi-storefronts-production.kajabi-cdn.com/kajabi-storefronts-production/<value>
|
|
33138
|
+
// so an absolute http(s) URL is structurally not a valid value for it.
|
|
33139
|
+
// Kajabi accepts one anyway, returns 200, echoes it back, ingests nothing,
|
|
33140
|
+
// and the course shows a broken image. Reported from a live client site
|
|
33141
|
+
// (DTI) via CS-1548 after it produced a false "done" report and three
|
|
33142
|
+
// failed cards.
|
|
33143
|
+
//
|
|
33144
|
+
// Anchored at the start so only an ABSOLUTE URL is refused; every relative
|
|
33145
|
+
// path — the valid shape — passes untouched. This does not enable setting a
|
|
33146
|
+
// thumbnail from a URL (that needs an upload path, CS-1548 item 4). It stops
|
|
33147
|
+
// us reporting success for a write that did not happen.
|
|
33148
|
+
argRejects: [
|
|
33149
|
+
{
|
|
33150
|
+
tool: "update_course",
|
|
33151
|
+
field: "thumbnail",
|
|
33152
|
+
pattern: "^\\s*https?://",
|
|
33153
|
+
message: "thumbnail takes a relative path inside Kajabi storage, not an external URL. Kajabi accepts a URL here, returns 200 and never ingests it, so the image would silently stay broken. Upload the asset to Kajabi first and pass the storage path it returns."
|
|
33154
|
+
},
|
|
33155
|
+
// CS-1554: `search_contacts` accepts `tag_id`, does not apply it, and
|
|
33156
|
+
// returns the FULL unfiltered contact list with `filters_applied: null`.
|
|
33157
|
+
// Reported first-hand from a live client site (DTI) while auditing which
|
|
33158
|
+
// contacts carried an opt-in tag. Corroborating evidence: Kajabi's own
|
|
33159
|
+
// REST reference filters contacts with `filter[has_tag_id]`, not a bare
|
|
33160
|
+
// `tag_id` — so the argument is dropped as unrecognised and the response
|
|
33161
|
+
// says so, if you read `filters_applied`.
|
|
33162
|
+
//
|
|
33163
|
+
// Worse than the thumbnail case it sits next to. A broken image is
|
|
33164
|
+
// visible; an unfiltered list that reads as filtered produces a confident
|
|
33165
|
+
// wrong ANSWER, which a human then acts on. That is the same shape as the
|
|
33166
|
+
// incident in CS-1554 where the agent told the customer content did not
|
|
33167
|
+
// exist when it did.
|
|
33168
|
+
//
|
|
33169
|
+
// Any value at all is refused: the field is not partially supported, it
|
|
33170
|
+
// is inert. `[\s\S]` matches every stringified scalar including 0 and
|
|
33171
|
+
// false, which a `.`-style pattern anchored on truthiness would miss.
|
|
33172
|
+
//
|
|
33173
|
+
// NOT REPRODUCED BY ME — I have no Kajabi connection. This rests on the
|
|
33174
|
+
// CS-1554 first-hand report plus the REST reference. The file's own bar is
|
|
33175
|
+
// "provably wrong, never a guess", so a reviewer with a live Kajabi
|
|
33176
|
+
// connection should confirm before this merges. If `tag_id` DOES filter
|
|
33177
|
+
// for some tenant, drop this rule and keep the scalar fix.
|
|
33178
|
+
{
|
|
33179
|
+
tool: "search_contacts",
|
|
33180
|
+
field: "tag_id",
|
|
33181
|
+
pattern: "[\\s\\S]",
|
|
33182
|
+
message: "tag_id is accepted by Kajabi and never applied \u2014 the call returns the FULL contact list with filters_applied: null, which reads as a filtered result and is not one. There is no working tag filter on this tool today. Use list_tags plus the per-contact tag data to determine membership, and treat any tag audit built on search_contacts(tag_id) as unfiltered."
|
|
33183
|
+
}
|
|
33184
|
+
]
|
|
33185
|
+
},
|
|
33186
|
+
"notion-cli": {
|
|
33187
|
+
// Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
|
|
33188
|
+
// Notion does not issue refresh_tokens, so `supportsRefresh: false` and
|
|
33189
|
+
// the refresh cron skips this provider entirely. Scopes are not part of
|
|
33190
|
+
// Notion's authorize URL contract; consent is governed by what the user
|
|
33191
|
+
// grants in the OAuth screen, so `defaultScopes` stays empty.
|
|
33192
|
+
// `owner=user` forces the user-OAuth variant (vs internal integration).
|
|
33193
|
+
// Requires OAUTH_NOTION_CLI_CLIENT_ID and OAUTH_NOTION_CLI_CLIENT_SECRET.
|
|
33194
|
+
definitionId: "notion-cli",
|
|
33195
|
+
authorizeUrl: "https://api.notion.com/v1/oauth/authorize",
|
|
33196
|
+
tokenUrl: "https://api.notion.com/v1/oauth/token",
|
|
33197
|
+
defaultScopes: [],
|
|
33198
|
+
supportsRefresh: false,
|
|
33199
|
+
extraAuthorizeParams: {
|
|
33200
|
+
owner: "user"
|
|
33201
|
+
},
|
|
33202
|
+
clientAuthMethod: "basic"
|
|
33203
|
+
},
|
|
33204
|
+
"xero": {
|
|
33205
|
+
definitionId: "xero",
|
|
33206
|
+
authorizeUrl: "https://login.xero.com/identity/connect/authorize",
|
|
33207
|
+
tokenUrl: "https://identity.xero.com/connect/token",
|
|
33208
|
+
revokeUrl: "https://identity.xero.com/connect/revocation",
|
|
33209
|
+
defaultScopes: [
|
|
33210
|
+
"openid",
|
|
33211
|
+
"profile",
|
|
33212
|
+
"email",
|
|
33213
|
+
"offline_access",
|
|
33214
|
+
// Granular scopes (required for apps created after March 2, 2026 —
|
|
33215
|
+
// do NOT revert to the broad `accounting.transactions` /
|
|
33216
|
+
// `accounting.contacts` scopes, Xero rejects the manifest).
|
|
33217
|
+
// The variant *without* `.read` is the read+write granular scope.
|
|
33218
|
+
"accounting.settings.read",
|
|
33219
|
+
// contacts: write enables agent-driven supplier/customer creation
|
|
33220
|
+
// (required for bill creation since a bill must reference a contact).
|
|
33221
|
+
"accounting.contacts",
|
|
33222
|
+
// invoices: write enables bill creation (Type=ACCPAY invoices) and
|
|
33223
|
+
// updates to sales invoices alongside the existing read access.
|
|
33224
|
+
"accounting.invoices",
|
|
33225
|
+
// attachments: write enables agents to attach the source PDF to a
|
|
33226
|
+
// bill at creation time. Read-only would force a follow-up manual
|
|
33227
|
+
// upload in Xero; write closes the loop.
|
|
33228
|
+
"accounting.attachments",
|
|
33229
|
+
// accounting.transactions.read → granular read-only replacements
|
|
33230
|
+
// for the surfaces we don't yet need write access on.
|
|
33231
|
+
"accounting.payments.read",
|
|
33232
|
+
"accounting.banktransactions.read",
|
|
33233
|
+
// manualjournals: write (CS-1439) enables the xero-broker to post
|
|
33234
|
+
// manual journals (reclasses, accruals, corrections) on approval. The
|
|
33235
|
+
// variant without `.read` is the read+write granular scope. Note: an
|
|
33236
|
+
// existing Xero install must RECONNECT for its token to carry this — the
|
|
33237
|
+
// broker posts journals with the agent's own OAuth access_token.
|
|
33238
|
+
"accounting.manualjournals",
|
|
33239
|
+
// accounting.reports.read → granular read-only replacements
|
|
33240
|
+
"accounting.reports.balancesheet.read",
|
|
33241
|
+
"accounting.reports.profitandloss.read",
|
|
33242
|
+
"accounting.reports.trialbalance.read",
|
|
33243
|
+
"accounting.reports.budgetsummary.read",
|
|
33244
|
+
"accounting.reports.banksummary.read",
|
|
33245
|
+
"accounting.reports.executivesummary.read",
|
|
33246
|
+
"accounting.reports.aged.read"
|
|
33247
|
+
],
|
|
33248
|
+
// Read-only variant (ENG-6170): the `.read` granular scope for every
|
|
33249
|
+
// surface, so a token granted under it cannot create bills/invoices or
|
|
33250
|
+
// mutate contacts/attachments. Used when an install is (re)connected with
|
|
33251
|
+
// `read_only: true` — e.g. to lock a production-books integration to reads
|
|
33252
|
+
// until per-vendor broker mediation (ENG-4922) gates its writes.
|
|
33253
|
+
readOnlyScopes: [
|
|
33254
|
+
"openid",
|
|
33255
|
+
"profile",
|
|
33256
|
+
"email",
|
|
33257
|
+
"offline_access",
|
|
33258
|
+
"accounting.settings.read",
|
|
33259
|
+
"accounting.contacts.read",
|
|
33260
|
+
"accounting.invoices.read",
|
|
33261
|
+
"accounting.attachments.read",
|
|
33262
|
+
"accounting.payments.read",
|
|
33263
|
+
"accounting.banktransactions.read",
|
|
33264
|
+
"accounting.manualjournals.read",
|
|
33265
|
+
"accounting.reports.balancesheet.read",
|
|
33266
|
+
"accounting.reports.profitandloss.read",
|
|
33267
|
+
"accounting.reports.trialbalance.read",
|
|
33268
|
+
"accounting.reports.budgetsummary.read",
|
|
33269
|
+
"accounting.reports.banksummary.read",
|
|
33270
|
+
"accounting.reports.executivesummary.read",
|
|
33271
|
+
"accounting.reports.aged.read"
|
|
33272
|
+
],
|
|
33273
|
+
supportsRefresh: true,
|
|
33274
|
+
extraAuthorizeParams: {},
|
|
33275
|
+
clientAuthMethod: "basic",
|
|
33276
|
+
userInfoUrl: "https://api.xero.com/connections"
|
|
33277
|
+
},
|
|
33278
|
+
// LinkedIn Ads (Marketing API). Confidential client with a static secret and
|
|
33279
|
+
// refresh tokens (~60-day access token, ~1-year rotating refresh token), so it
|
|
33280
|
+
// mirrors the Xero shape: clientAuthMethod 'body', supportsRefresh true, no
|
|
33281
|
+
// PKCE. The shared authorize/callback/refresh machinery handles the rest.
|
|
33282
|
+
//
|
|
33283
|
+
// The single shared app is Augmented Team's own LinkedIn developer app
|
|
33284
|
+
// (client id/secret in OAUTH_LINKEDIN_ADS_CLIENT_ID / _SECRET) - every org
|
|
33285
|
+
// connects its own ad accounts against it; only the per-connection tokens
|
|
33286
|
+
// differ. Register `${API_PUBLIC_URL}/integrations/oauth/callback` as an
|
|
33287
|
+
// Authorized redirect URL on the LinkedIn app.
|
|
33288
|
+
//
|
|
33289
|
+
// NOTE (linkedin-verify): the grantable scope set depends on which LinkedIn
|
|
33290
|
+
// API products the app is approved for (Advertising API, Lead Sync API,
|
|
33291
|
+
// Community Management API). Keep this list to scopes the app actually holds -
|
|
33292
|
+
// requesting an ungranted scope makes LinkedIn reject the authorize request.
|
|
33293
|
+
"linkedin-ads": {
|
|
33294
|
+
definitionId: "linkedin-ads",
|
|
33295
|
+
authorizeUrl: "https://www.linkedin.com/oauth/v2/authorization",
|
|
33296
|
+
tokenUrl: "https://www.linkedin.com/oauth/v2/accessToken",
|
|
33297
|
+
// ENG-7897: this list is EXACTLY the Advertising API scope set, because
|
|
33298
|
+
// Advertising API is the only product added to the Brand Ninja app. Every
|
|
33299
|
+
// other scope we used to request here belonged to a product the app does
|
|
33300
|
+
// not hold, and LinkedIn rejects the ENTIRE authorize request with
|
|
33301
|
+
// `unauthorized_scope_error` if ANY single requested scope is ungranted -
|
|
33302
|
+
// so five unapproved scopes bricked the connect for every customer, on
|
|
33303
|
+
// every environment, rather than merely disabling five features.
|
|
33304
|
+
//
|
|
33305
|
+
// Scopes deliberately NOT requested, and the product each needs first:
|
|
33306
|
+
// openid / profile / email -> Sign In with LinkedIn (OpenID Connect)
|
|
33307
|
+
// r_organization_social -> Community Management API
|
|
33308
|
+
// r_marketing_leadgen_automation -> Lead Sync API
|
|
33309
|
+
//
|
|
33310
|
+
// Dropping the OIDC trio costs only the `Connected as <name>` label: the
|
|
33311
|
+
// userInfoUrl fetch in the callback is already best-effort and falls back
|
|
33312
|
+
// to a plain 'Connected'. Do NOT re-add any of these until the matching
|
|
33313
|
+
// product shows under "Added products" on the app - use the
|
|
33314
|
+
// OAUTH_LINKEDIN_ADS_SCOPES override below to widen without a deploy once
|
|
33315
|
+
// an approval lands, and only then promote it into this constant.
|
|
33316
|
+
defaultScopes: [
|
|
33317
|
+
// Advertising API: read + manage campaigns/creatives, and reporting.
|
|
33318
|
+
"r_ads",
|
|
33319
|
+
"rw_ads",
|
|
33320
|
+
"r_ads_reporting"
|
|
33321
|
+
],
|
|
33322
|
+
// Read-only variant: reporting only, no campaign/creative mutation. Used
|
|
33323
|
+
// when an install is (re)connected with read_only: true. Same product
|
|
33324
|
+
// constraint as above - `r_organization_social` was removed with it.
|
|
33325
|
+
readOnlyScopes: [
|
|
33326
|
+
"r_ads",
|
|
33327
|
+
"r_ads_reporting"
|
|
33328
|
+
],
|
|
33329
|
+
supportsRefresh: true,
|
|
33330
|
+
extraAuthorizeParams: {},
|
|
33331
|
+
clientAuthMethod: "body",
|
|
33332
|
+
userInfoUrl: "https://api.linkedin.com/v2/userinfo"
|
|
33333
|
+
}
|
|
33334
|
+
};
|
|
33335
|
+
OAUTH_PROVIDERS["xero-control-plane"] = {
|
|
33336
|
+
...OAUTH_PROVIDERS["xero"]
|
|
33337
|
+
};
|
|
33338
|
+
|
|
32886
33339
|
// ../core/dist/integrations/composio-account-probe.js
|
|
32887
33340
|
var BODY_MAX_BYTES = 1024 * 1024;
|
|
32888
33341
|
|