@integrity-labs/agt-cli 0.28.1111 → 0.28.1113
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-4PTXGOVO.js → chunk-JX7ZXCNZ.js} +2 -2
- package/dist/{chunk-TZM6YVE7.js → chunk-OBZB6MUE.js} +105 -9
- package/dist/chunk-OBZB6MUE.js.map +1 -0
- package/dist/{chunk-2UHVPGB3.js → chunk-TXNVB3DN.js} +4 -4
- package/dist/{claude-pair-runtime-L7KXG3DD.js → claude-pair-runtime-GL67TYXC.js} +2 -2
- package/dist/lib/manager-worker.js +16 -16
- package/dist/mcp/direct-chat-channel.js +332 -14
- package/dist/mcp/index.js +340 -38
- package/dist/mcp/origami.js +104 -8
- package/dist/mcp/slack-channel.js +340 -14
- package/dist/mcp/telegram-channel.js +472 -70
- package/dist/{persistent-session-HSYN3SQ2.js → persistent-session-6KIQF4O7.js} +3 -3
- package/dist/{responsiveness-probe-MO4L5O6F.js → responsiveness-probe-GGJNITME.js} +3 -3
- package/dist/{session-auth-dead-ZKI4XCSI.js → session-auth-dead-LMCPZJ4K.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-TZM6YVE7.js.map +0 -1
- /package/dist/{chunk-4PTXGOVO.js.map → chunk-JX7ZXCNZ.js.map} +0 -0
- /package/dist/{chunk-2UHVPGB3.js.map → chunk-TXNVB3DN.js.map} +0 -0
- /package/dist/{claude-pair-runtime-L7KXG3DD.js.map → claude-pair-runtime-GL67TYXC.js.map} +0 -0
- /package/dist/{persistent-session-HSYN3SQ2.js.map → persistent-session-6KIQF4O7.js.map} +0 -0
- /package/dist/{responsiveness-probe-MO4L5O6F.js.map → responsiveness-probe-GGJNITME.js.map} +0 -0
- /package/dist/{session-auth-dead-ZKI4XCSI.js.map → session-auth-dead-LMCPZJ4K.js.map} +0 -0
|
@@ -22926,6 +22926,86 @@ var init_slack_block_kit_runtime = __esm({
|
|
|
22926
22926
|
}
|
|
22927
22927
|
});
|
|
22928
22928
|
|
|
22929
|
+
// src/telegram-tap-reply.ts
|
|
22930
|
+
var telegram_tap_reply_exports = {};
|
|
22931
|
+
__export(telegram_tap_reply_exports, {
|
|
22932
|
+
ANSWER_BUDGET_MS: () => ANSWER_BUDGET_MS,
|
|
22933
|
+
REFUSED_OUTCOMES: () => REFUSED_OUTCOMES,
|
|
22934
|
+
TELEGRAM_ALERT_MAX: () => TELEGRAM_ALERT_MAX,
|
|
22935
|
+
answerApprovalTap: () => answerApprovalTap,
|
|
22936
|
+
classifyResolve: () => classifyResolve,
|
|
22937
|
+
fitAlert: () => fitAlert,
|
|
22938
|
+
tappersOwnChatId: () => tappersOwnChatId
|
|
22939
|
+
});
|
|
22940
|
+
function classifyResolve(result) {
|
|
22941
|
+
if (result.kind === "threw") return { kind: "failed", detail: `threw: ${result.error}` };
|
|
22942
|
+
const { httpOk, status, body } = result;
|
|
22943
|
+
if (httpOk && body.ok === false && typeof body.outcome === "string" && REFUSED_OUTCOMES.has(body.outcome)) {
|
|
22944
|
+
const text = typeof body.message === "string" && body.message.trim() ? body.message.trim() : GENERIC_REFUSAL;
|
|
22945
|
+
return { kind: "refused", text };
|
|
22946
|
+
}
|
|
22947
|
+
if (!httpOk || !body.ok) return { kind: "failed", detail: `http=${status} reason=${body.reason ?? "unknown"}` };
|
|
22948
|
+
return { kind: "decided" };
|
|
22949
|
+
}
|
|
22950
|
+
function fitAlert(text) {
|
|
22951
|
+
if (text.length <= TELEGRAM_ALERT_MAX) return { alert: text, truncated: false };
|
|
22952
|
+
const room = TELEGRAM_ALERT_MAX - 1;
|
|
22953
|
+
const cut = text.slice(0, room);
|
|
22954
|
+
const space = cut.lastIndexOf(" ");
|
|
22955
|
+
const head = (space > room / 2 ? cut.slice(0, space) : cut).trimEnd();
|
|
22956
|
+
return { alert: `${head}\u2026`, truncated: true };
|
|
22957
|
+
}
|
|
22958
|
+
function tappersOwnChatId(cb) {
|
|
22959
|
+
const chat = cb.message?.chat;
|
|
22960
|
+
return chat && chat.type === "private" && chat.id === cb.from.id ? chat.id : null;
|
|
22961
|
+
}
|
|
22962
|
+
async function answerApprovalTap(deps) {
|
|
22963
|
+
const budget = deps.budgetMs ?? ANSWER_BUDGET_MS;
|
|
22964
|
+
const pending = deps.resolve().catch((err) => ({ kind: "threw", error: err?.message ?? String(err) }));
|
|
22965
|
+
let timer;
|
|
22966
|
+
const first = await Promise.race([
|
|
22967
|
+
pending.then((r) => ({ r })),
|
|
22968
|
+
new Promise((resolve3) => {
|
|
22969
|
+
timer = setTimeout(() => resolve3(null), budget);
|
|
22970
|
+
})
|
|
22971
|
+
]);
|
|
22972
|
+
if (timer) clearTimeout(timer);
|
|
22973
|
+
let answeredEarly = false;
|
|
22974
|
+
if (first === null) {
|
|
22975
|
+
answeredEarly = true;
|
|
22976
|
+
await deps.answer("Got it", false);
|
|
22977
|
+
}
|
|
22978
|
+
const outcome = classifyResolve(first ? first.r : await pending);
|
|
22979
|
+
switch (outcome.kind) {
|
|
22980
|
+
case "decided":
|
|
22981
|
+
if (!answeredEarly) await deps.answer("Got it", false);
|
|
22982
|
+
await deps.clearKeyboard();
|
|
22983
|
+
break;
|
|
22984
|
+
case "refused": {
|
|
22985
|
+
const { alert, truncated } = fitAlert(outcome.text);
|
|
22986
|
+
if (!answeredEarly) await deps.answer(alert, true);
|
|
22987
|
+
if ((answeredEarly || truncated) && deps.replyPrivately) await deps.replyPrivately(outcome.text);
|
|
22988
|
+
deps.log(`refused (answeredEarly=${answeredEarly}, truncated=${truncated}, private=${deps.replyPrivately !== null})`);
|
|
22989
|
+
break;
|
|
22990
|
+
}
|
|
22991
|
+
case "failed":
|
|
22992
|
+
if (!answeredEarly) await deps.answer("Could not record your choice", false);
|
|
22993
|
+
deps.log(`resolve failed: ${outcome.detail}`);
|
|
22994
|
+
break;
|
|
22995
|
+
}
|
|
22996
|
+
return { ...outcome, answeredEarly };
|
|
22997
|
+
}
|
|
22998
|
+
var TELEGRAM_ALERT_MAX, ANSWER_BUDGET_MS, REFUSED_OUTCOMES, GENERIC_REFUSAL;
|
|
22999
|
+
var init_telegram_tap_reply = __esm({
|
|
23000
|
+
"src/telegram-tap-reply.ts"() {
|
|
23001
|
+
"use strict";
|
|
23002
|
+
TELEGRAM_ALERT_MAX = 200;
|
|
23003
|
+
ANSWER_BUDGET_MS = 8e3;
|
|
23004
|
+
REFUSED_OUTCOMES = /* @__PURE__ */ new Set(["authority_refused", "sod_refused", "interaction_refused"]);
|
|
23005
|
+
GENERIC_REFUSAL = "You can't decide this request.";
|
|
23006
|
+
}
|
|
23007
|
+
});
|
|
23008
|
+
|
|
22929
23009
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/core.js
|
|
22930
23010
|
var NEVER = Object.freeze({
|
|
22931
23011
|
status: "aborted"
|
|
@@ -30923,19 +31003,190 @@ function resolveGhostReplyMode(opts) {
|
|
|
30923
31003
|
if (cached2 !== void 0) return cached2;
|
|
30924
31004
|
return "off";
|
|
30925
31005
|
}
|
|
31006
|
+
var MAINTENANCE_FLAG_KEY = "platform-maintenance-mode";
|
|
31007
|
+
function parseMaintenanceMode(raw) {
|
|
31008
|
+
if (raw === void 0) return void 0;
|
|
31009
|
+
const v = raw.trim().toLowerCase();
|
|
31010
|
+
if (v === "") return void 0;
|
|
31011
|
+
if (v === "off" || v === "tools-offline" || v === "fleet-offline") return v;
|
|
31012
|
+
const b = envBoolean(raw);
|
|
31013
|
+
if (b === true) return "fleet-offline";
|
|
31014
|
+
if (b === false) return "off";
|
|
31015
|
+
return void 0;
|
|
31016
|
+
}
|
|
31017
|
+
function cachedMaintenanceMode(path) {
|
|
31018
|
+
try {
|
|
31019
|
+
if (!existsSync2(path)) return void 0;
|
|
31020
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
31021
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
31022
|
+
const flags = parsed.flags;
|
|
31023
|
+
if (!flags || typeof flags !== "object") return void 0;
|
|
31024
|
+
const value = flags[MAINTENANCE_FLAG_KEY];
|
|
31025
|
+
if (typeof value === "string") return parseMaintenanceMode(value);
|
|
31026
|
+
if (typeof value === "boolean") return value ? "fleet-offline" : "off";
|
|
31027
|
+
return void 0;
|
|
31028
|
+
} catch {
|
|
31029
|
+
return void 0;
|
|
31030
|
+
}
|
|
31031
|
+
}
|
|
31032
|
+
function resolveMaintenanceMode(opts) {
|
|
31033
|
+
const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath());
|
|
31034
|
+
if (cached2 !== void 0) return cached2;
|
|
31035
|
+
return "off";
|
|
31036
|
+
}
|
|
30926
31037
|
|
|
30927
31038
|
// src/maintenance-mode.ts
|
|
30928
|
-
var FLAG_KEY = "platform-maintenance-mode";
|
|
30929
31039
|
var MAINTENANCE_OFFLINE_MESSAGE = "The Ninjafy platform is offline for scheduled maintenance right now, so I can't pick this up \u2014 please try again shortly.";
|
|
30930
31040
|
function isMaintenanceModeActive(opts) {
|
|
30931
|
-
return
|
|
30932
|
-
key: FLAG_KEY,
|
|
30933
|
-
envVar: "",
|
|
30934
|
-
defaultValue: false,
|
|
31041
|
+
return resolveMaintenanceMode({
|
|
30935
31042
|
cachePath: opts?.cachePath,
|
|
30936
31043
|
env: opts?.env
|
|
30937
|
-
});
|
|
31044
|
+
}) === "fleet-offline";
|
|
31045
|
+
}
|
|
31046
|
+
|
|
31047
|
+
// src/maintenance-tools-offline.ts
|
|
31048
|
+
function isToolsOfflineActive(opts) {
|
|
31049
|
+
const mode = resolveMaintenanceMode({ cachePath: opts?.cachePath, env: opts?.env });
|
|
31050
|
+
return mode === "tools-offline" || mode === "fleet-offline";
|
|
31051
|
+
}
|
|
31052
|
+
var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
|
|
31053
|
+
// Tasks.
|
|
31054
|
+
"kanban_add",
|
|
31055
|
+
"kanban_update",
|
|
31056
|
+
"kanban_move",
|
|
31057
|
+
"kanban_done",
|
|
31058
|
+
"kanban_reopen",
|
|
31059
|
+
"kanban_assign",
|
|
31060
|
+
"kanban_reassign",
|
|
31061
|
+
"kanban_recall",
|
|
31062
|
+
"kanban_respond",
|
|
31063
|
+
"kanban_progress",
|
|
31064
|
+
"kanban_log",
|
|
31065
|
+
"kanban_assign_project",
|
|
31066
|
+
"assign_kanban_to_human",
|
|
31067
|
+
// Canvases. `dashboards_request_refresh` is gated because it enqueues
|
|
31068
|
+
// server-side work, not because it renders anything itself.
|
|
31069
|
+
"dashboards_upsert",
|
|
31070
|
+
"dashboards_persist_widget",
|
|
31071
|
+
"dashboards_request_refresh",
|
|
31072
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
31073
|
+
// FORWARDED writes (ENG-11436 / CodeRabbit on #6903). These reach the agent
|
|
31074
|
+
// through `registerForwardedApiTools()` -> `server.registerTool`, from
|
|
31075
|
+
// `packages/api/src/routes/mcp-tools.ts`, with names resolved at RUNTIME. They
|
|
31076
|
+
// are gated only because index.ts now wraps that method too; before that, this
|
|
31077
|
+
// whole block would have been a list the gate could not see.
|
|
31078
|
+
//
|
|
31079
|
+
// CodeRabbit named five (workflow_update, workflow_run_{start,phase,end},
|
|
31080
|
+
// memory_delete). Enumerating the surface to check them found 37 of the 50
|
|
31081
|
+
// forwarded tools unclassified, so the five were a subset — these are all of
|
|
31082
|
+
// the writes among them. The test now classifies that surface exhaustively, so
|
|
31083
|
+
// the next addition fails CI rather than slipping through.
|
|
31084
|
+
"composio_upload_file",
|
|
31085
|
+
"memory_delete",
|
|
31086
|
+
"memory_save",
|
|
31087
|
+
"onboarding_advance",
|
|
31088
|
+
"onboarding_provision_skills",
|
|
31089
|
+
"onboarding_provision_tasks",
|
|
31090
|
+
"onboarding_record_manager_contact",
|
|
31091
|
+
"request_batch_approval",
|
|
31092
|
+
"request_integration_access",
|
|
31093
|
+
"skill_contribute_fragment",
|
|
31094
|
+
"skill_create",
|
|
31095
|
+
"skill_delete",
|
|
31096
|
+
"skill_improve",
|
|
31097
|
+
"skill_propose_revision",
|
|
31098
|
+
"skill_publish_to_marketplace",
|
|
31099
|
+
"skill_update",
|
|
31100
|
+
"submit_performance_review",
|
|
31101
|
+
"workflow_run_end",
|
|
31102
|
+
"workflow_run_phase",
|
|
31103
|
+
"workflow_run_start",
|
|
31104
|
+
"workflow_update",
|
|
31105
|
+
// CHANNEL-SERVER write (Obi Wan `b3537c1e8495` on #6903). Dispatched by NAME in
|
|
31106
|
+
// slack-channel.ts / telegram-channel.ts / direct-chat-channel.ts, which are
|
|
31107
|
+
// separate processes that never call server.tool — so this one is enforced by
|
|
31108
|
+
// `refuseIfToolsOffline` at each dispatch point, not by the registration
|
|
31109
|
+
// wrapper. It inserts a `pending_interaction` row, the same durable
|
|
31110
|
+
// human-actionable record that gates request_buttons / request_form.
|
|
31111
|
+
"channel_request_input",
|
|
31112
|
+
// `slack.ask_user` is the SAME write under a dotted name, and it was missed
|
|
31113
|
+
// because the test's enumerator could not see dotted names at all (Obi Wan
|
|
31114
|
+
// `122ac55ff278` / `d7245eddd77f` on #6903). handleAskUser calls
|
|
31115
|
+
// runtime.createPendingInteraction -> POST /host/pending-interactions ->
|
|
31116
|
+
// inserts `pending_interaction`. Ungated, an agent on a /loop files the row,
|
|
31117
|
+
// the restore discards it, and the agent blocks forever on a resolution that
|
|
31118
|
+
// no longer exists — the exact failure the entry above is gated for.
|
|
31119
|
+
//
|
|
31120
|
+
// Verified it is the ONLY dotted channel tool that writes: createPendingInteraction
|
|
31121
|
+
// is CALLED once in the three channel servers (slack-channel.ts:8053);
|
|
31122
|
+
// slack-block-kit-runtime.ts only re-exports it.
|
|
31123
|
+
"slack.ask_user",
|
|
31124
|
+
// Workflows. FORWARDED, not local — it reaches the agent through
|
|
31125
|
+
// `server.registerTool` in registerForwardedApiTools, so it is gated only
|
|
31126
|
+
// because that method is now wrapped too (CodeRabbit on #6903).
|
|
31127
|
+
"workflow_create",
|
|
31128
|
+
// Knowledge.
|
|
31129
|
+
"knowledge_add",
|
|
31130
|
+
"knowledge_update",
|
|
31131
|
+
"knowledge_delete",
|
|
31132
|
+
// Projects and project tagging.
|
|
31133
|
+
"projects_create",
|
|
31134
|
+
"projects_update",
|
|
31135
|
+
"artefacts_assign_project",
|
|
31136
|
+
// Routines.
|
|
31137
|
+
"routine_propose",
|
|
31138
|
+
"routine_revise",
|
|
31139
|
+
"routine_retire",
|
|
31140
|
+
"submit_routine_output",
|
|
31141
|
+
// Standing rules.
|
|
31142
|
+
"rule_add",
|
|
31143
|
+
"rule_remove",
|
|
31144
|
+
// Scheduled work.
|
|
31145
|
+
"schedule_create",
|
|
31146
|
+
"schedule_update",
|
|
31147
|
+
"schedule_delete",
|
|
31148
|
+
// Requests that file a durable record a human is expected to act on. Filing
|
|
31149
|
+
// one mid-window is worse than refusing it: the record is discarded by the
|
|
31150
|
+
// restore, so the agent would have promised a human a follow-up that no
|
|
31151
|
+
// longer exists anywhere.
|
|
31152
|
+
"request_approval",
|
|
31153
|
+
"request_buttons",
|
|
31154
|
+
"request_channel_access",
|
|
31155
|
+
"request_form",
|
|
31156
|
+
"request_reconnect",
|
|
31157
|
+
"request_feature",
|
|
31158
|
+
"request_feature_withdraw",
|
|
31159
|
+
// ADR-0025 approval tools, registered by `approval-tools.ts` against the same
|
|
31160
|
+
// server object, so the wrapper in index.ts covers them too.
|
|
31161
|
+
"cancel_approval",
|
|
31162
|
+
// Status / identity.
|
|
31163
|
+
"status_standup",
|
|
31164
|
+
"status_update",
|
|
31165
|
+
"update_identity",
|
|
31166
|
+
"update_timezone",
|
|
31167
|
+
// Fleet changes.
|
|
31168
|
+
"recruit_agent",
|
|
31169
|
+
"integration_improve"
|
|
31170
|
+
]);
|
|
31171
|
+
function toolsOfflineRefusalText(toolName) {
|
|
31172
|
+
return `Ninjafy is in a control-plane maintenance window, so \`${toolName}\` is unavailable right now. Nothing was created, changed or saved. Do NOT retry this call, and do not try a different Ninjafy tool to work around it \u2014 every tool that writes to the control plane is refusing for the same reason, and the window is short. Tell the person plainly that you cannot save this at the moment and ask them to bring it back afterwards; carry on with anything that does not need saving.`;
|
|
31173
|
+
}
|
|
31174
|
+
function refuseIfToolsOffline(name, opts) {
|
|
31175
|
+
if (!MAINTENANCE_GATED_TOOLS.has(name)) return null;
|
|
31176
|
+
const isActive = opts?.isActive ?? (() => isToolsOfflineActive());
|
|
31177
|
+
if (!isActive()) return null;
|
|
31178
|
+
return { content: [{ type: "text", text: toolsOfflineRefusalText(name) }] };
|
|
31179
|
+
}
|
|
31180
|
+
function shouldSendToolsOfflineNotice(opts) {
|
|
31181
|
+
const active = opts.isActive ?? (() => isToolsOfflineActive());
|
|
31182
|
+
if (!active()) return false;
|
|
31183
|
+
const now = opts.now ?? Date.now();
|
|
31184
|
+
const last = opts.cache.get(opts.key);
|
|
31185
|
+
if (last !== void 0 && now - last < opts.cooldownMs) return false;
|
|
31186
|
+
opts.cache.set(opts.key, now);
|
|
31187
|
+
return true;
|
|
30938
31188
|
}
|
|
31189
|
+
var TOOLS_OFFLINE_USER_NOTICE = "Heads up \u2014 Ninjafy is in a maintenance window right now, so I can't save anything: no tasks, canvases, notes or scheduled items. I can still talk things through, and you can ask me to set it up once maintenance is done.";
|
|
30939
31190
|
|
|
30940
31191
|
// ../core/dist/types/agent.js
|
|
30941
31192
|
var FRAMEWORK_MODEL_SLOTS = {
|
|
@@ -34009,6 +34260,51 @@ var OAUTH_PROVIDERS = {
|
|
|
34009
34260
|
}
|
|
34010
34261
|
]
|
|
34011
34262
|
},
|
|
34263
|
+
"otter": {
|
|
34264
|
+
// CS-2029: Otter.ai's official MCP — remote streamable-HTTP at
|
|
34265
|
+
// https://mcp.otter.ai/mcp. Same Granola/Kajabi shape: OAuth 2.0
|
|
34266
|
+
// authorization-code with PKCE (S256) and a public client
|
|
34267
|
+
// (token_endpoint_auth_method 'none') issued via Dynamic Client
|
|
34268
|
+
// Registration (RFC 7591). Values below are taken verbatim from the
|
|
34269
|
+
// metadata, not inferred:
|
|
34270
|
+
// - https://mcp.otter.ai/.well-known/oauth-protected-resource names the
|
|
34271
|
+
// AS as https://otter.ai/ and advertises scopes profile:read,
|
|
34272
|
+
// conversations:read;
|
|
34273
|
+
// - https://otter.ai/.well-known/oauth-authorization-server gives
|
|
34274
|
+
// authorize /oauth2/authorize, token /oauth/token, revoke
|
|
34275
|
+
// /oauth/revoke_token, register /oauth/register, PKCE S256, `none`,
|
|
34276
|
+
// and the refresh_token grant.
|
|
34277
|
+
// NOTE the authorize path is /oauth2/authorize while token/revoke live
|
|
34278
|
+
// under /oauth/ — that is Otter's own split, do not "normalise" it.
|
|
34279
|
+
// The prod client was registered 2026-10-10 against /oauth/register with
|
|
34280
|
+
// scope 'profile:read conversations:read' and BOTH API callbacks,
|
|
34281
|
+
// https://api.augmented.team/... and https://api.ninjafy.com/integrations/oauth/callback,
|
|
34282
|
+
// because redirect_uri follows API_PUBLIC_URL and the ENG-11060 flip makes
|
|
34283
|
+
// the ninjafy name canonical; Otter returns no RFC 7592 handle, so a redirect
|
|
34284
|
+
// cannot be added later without a new client and a re-consent per connection.
|
|
34285
|
+
// OAUTH_OTTER_CLIENT_ID is set from its output (docs/runbooks/otter-dogfood.md). Otter advertises ONLY
|
|
34286
|
+
// read scopes, so this integration is read-only by construction: there is
|
|
34287
|
+
// no write scope to request even by mistake.
|
|
34288
|
+
definitionId: "otter",
|
|
34289
|
+
authorizeUrl: "https://otter.ai/oauth2/authorize",
|
|
34290
|
+
tokenUrl: "https://otter.ai/oauth/token",
|
|
34291
|
+
revokeUrl: "https://otter.ai/oauth/revoke_token",
|
|
34292
|
+
defaultScopes: ["profile:read", "conversations:read"],
|
|
34293
|
+
supportsRefresh: true,
|
|
34294
|
+
extraAuthorizeParams: {},
|
|
34295
|
+
clientAuthMethod: "body",
|
|
34296
|
+
pkce: "S256",
|
|
34297
|
+
publicClient: true,
|
|
34298
|
+
mcpUrl: "https://mcp.otter.ai/mcp",
|
|
34299
|
+
// Curated surface (matches the catalog seed's defined_scopes[].tools).
|
|
34300
|
+
// These are the server's three tools, all read-only. The names are
|
|
34301
|
+
// Otter's own, quoted from the tools' descriptions ("use `otter_search`
|
|
34302
|
+
// first", "call otter_get_user_info() first"), not Composio's wrapped
|
|
34303
|
+
// OTTER_AI_MCP_* slugs. An allowlisted name the server does not advertise
|
|
34304
|
+
// is logged and SKIPPED by the proxy, so confirm against the first live
|
|
34305
|
+
// tools/list after a real consent (runbook Step 3).
|
|
34306
|
+
toolAllowlist: ["otter_get_user_info", "otter_search", "otter_fetch"]
|
|
34307
|
+
},
|
|
34012
34308
|
"notion-cli": {
|
|
34013
34309
|
// Notion's public OAuth app. Tokens are workspace-scoped and long-lived —
|
|
34014
34310
|
// Notion does not issue refresh_tokens, so `supportsRefresh: false` and
|
|
@@ -34642,6 +34938,34 @@ var INTEGRATION_REGISTRY = [
|
|
|
34642
34938
|
docs_url: "https://help.kajabi.com/articles/api-integrations/connect-kajabi-to-claude-or-chatgpt",
|
|
34643
34939
|
beta: true
|
|
34644
34940
|
},
|
|
34941
|
+
{
|
|
34942
|
+
id: "otter",
|
|
34943
|
+
name: "Otter.ai",
|
|
34944
|
+
category: "knowledge",
|
|
34945
|
+
description: "Read Otter.ai meetings: search recordings by date, attendee or topic, and read summaries, action items and full speaker-attributed transcripts.",
|
|
34946
|
+
// CS-2029: same remote streamable-HTTP MCP + OAuth pattern as Granola/Kajabi.
|
|
34947
|
+
// Otter's server implements the MCP discovery chain (RFC 9728 protected-
|
|
34948
|
+
// resource metadata at mcp.otter.ai → RFC 8414 AS metadata at otter.ai →
|
|
34949
|
+
// RFC 7591 registration); auth is OAuth 2.0 authorization-code with PKCE
|
|
34950
|
+
// (S256) and a public client registered via one-time Dynamic Client
|
|
34951
|
+
// Registration (scripts/dcr-register.ts against https://otter.ai/oauth/register
|
|
34952
|
+
// → OAUTH_OTTER_CLIENT_ID, --scope 'profile:read conversations:read').
|
|
34953
|
+
// Consent is brokered by the webapp through the shared
|
|
34954
|
+
// /integrations/oauth/authorize → /callback path, and the access_token
|
|
34955
|
+
// reaches the host via the generic remote-oauth-proxy
|
|
34956
|
+
// (OAUTH_PROVIDERS.otter.mcpUrl). Read-only by construction: Otter
|
|
34957
|
+
// advertises no write scope.
|
|
34958
|
+
supported_auth_types: ["oauth2"],
|
|
34959
|
+
capabilities: [
|
|
34960
|
+
{ id: "otter:read", name: "Read Meetings & Transcripts", description: "Find recorded meetings by date, attendee, topic or title, and read their summaries, action items, outlines and full transcripts (otter_get_user_info, otter_search, otter_fetch)", access: "read" }
|
|
34961
|
+
],
|
|
34962
|
+
// Otter's own Help Center article for the MCP server (CodeRabbit, #6906). It
|
|
34963
|
+
// exists even though help.otter.ai answers 403 to non-browser clients; the
|
|
34964
|
+
// help-centre API confirmed it (title "Otter MCP Server"). The MCP endpoint
|
|
34965
|
+
// itself is a JSON-RPC URL, not a page (Obi Wan 8305072d5414).
|
|
34966
|
+
docs_url: "https://help.otter.ai/hc/en-us/articles/35287607569687-Otter-MCP-Server",
|
|
34967
|
+
beta: true
|
|
34968
|
+
},
|
|
34645
34969
|
{
|
|
34646
34970
|
id: "anchor-browser",
|
|
34647
34971
|
name: "Anchor Browser",
|
|
@@ -39333,14 +39657,37 @@ var FLAG_REGISTRY = [
|
|
|
39333
39657
|
},
|
|
39334
39658
|
{
|
|
39335
39659
|
key: "platform-maintenance-mode",
|
|
39336
|
-
description: 'Platform-wide maintenance mode (ENG-6506)
|
|
39337
|
-
flagType: "
|
|
39338
|
-
|
|
39339
|
-
//
|
|
39340
|
-
//
|
|
39341
|
-
//
|
|
39342
|
-
//
|
|
39343
|
-
|
|
39660
|
+
description: 'Platform-wide maintenance mode (ENG-6506; widened to modes by ENG-11436). off = normal operation (ships dark; channels behave exactly as today). tools-offline = every agent KEEPS conversing but every Augmented tool that WRITES to the control plane refuses with a reason, so nothing is created during a window whose database restore would discard it; reads are untouched. fleet-offline = the previous boolean-true behaviour AND the write gate: admitted human inbound gets a "we are offline for maintenance" notice instead of being dispatched, for whole-platform downtime such as DB upgrades. The modes are CUMULATIVE \u2014 fleet-offline gates writes too, because silencing channel inbound does not stop a /loop or a scheduled task filing work. Read host-side per inbound and per tool call from the flags cache (no MCP restart). (Proactive "back online" follow-up is the ENG-6508 fast-follow, not this flag.)',
|
|
39661
|
+
flagType: "enum",
|
|
39662
|
+
allowedValues: ["off", "tools-offline", "fleet-offline"],
|
|
39663
|
+
// Declared safe value is `off` (normal operation). Fail-safe direction: a
|
|
39664
|
+
// flag-DB read error or absent flag map must never silence the fleet OR take
|
|
39665
|
+
// its write tools away on its own — agents stay fully functional unless an
|
|
39666
|
+
// operator deliberately sets a mode from the admin Feature Flags page. No
|
|
39667
|
+
// envVar: net-new control, no pre-flags env gate to migrate.
|
|
39668
|
+
//
|
|
39669
|
+
// ENG-11436 widened this from a boolean rather than adding a second flag, so
|
|
39670
|
+
// there is ONE operator control whose modes are mutually exclusive by type
|
|
39671
|
+
// instead of two similar booleans with four combinations.
|
|
39672
|
+
//
|
|
39673
|
+
// The host resolver GRANDFATHERS a boolean-shaped cache value
|
|
39674
|
+
// (true -> fleet-offline, false -> off), and the DIRECTION it protects is
|
|
39675
|
+
// narrower than an earlier version of this comment claimed (Obi Wan
|
|
39676
|
+
// `e3f0d2c154b9` on #6903). It said "an older manager still writing a boolean
|
|
39677
|
+
// must not make a central flip a silent no-op". That situation cannot arise:
|
|
39678
|
+
// once this registry change deploys, normalizeFlagValue only ever yields one
|
|
39679
|
+
// of the three strings, feature-flags-host.ts writes the heartbeat map
|
|
39680
|
+
// verbatim, so no manager writes a boolean again. What grandfathering
|
|
39681
|
+
// actually covers is a NEW MCP reading a STALE PRE-DEPLOY cache that still
|
|
39682
|
+
// holds one.
|
|
39683
|
+
//
|
|
39684
|
+
// The direction that DOES happen is not handled and cannot be: an OLD MCP
|
|
39685
|
+
// reads the new string with resolveHostBooleanFlag, gets undefined, and keeps
|
|
39686
|
+
// dispatching. That is a real regression against ENG-6506 until the fleet
|
|
39687
|
+
// converges, and it is why the runbook makes convergence a deploy-order
|
|
39688
|
+
// PRECONDITION rather than offering a boolean fallback — there is none, since
|
|
39689
|
+
// an enum flag rejects `{value: true}` with a 400.
|
|
39690
|
+
defaultValue: "off",
|
|
39344
39691
|
// Flipping this takes the ENTIRE fleet offline to end users; a mis-flip is a
|
|
39345
39692
|
// platform-wide availability incident, so mutations require explicit
|
|
39346
39693
|
// confirmation (the admin flip-reach modal).
|
|
@@ -43937,6 +44284,42 @@ var TELEGRAM_ACCOUNT_WARN_COOLDOWN_MS = (() => {
|
|
|
43937
44284
|
const n = raw ? Number(raw) : NaN;
|
|
43938
44285
|
return Number.isFinite(n) && n >= 0 ? n * 1e3 : 24 * 60 * 60 * 1e3;
|
|
43939
44286
|
})();
|
|
44287
|
+
var TELEGRAM_TOOLS_OFFLINE_NOTICE_CACHE = /* @__PURE__ */ new Map();
|
|
44288
|
+
var TELEGRAM_TOOLS_OFFLINE_COOLDOWN_MS = (() => {
|
|
44289
|
+
const raw = process.env["TELEGRAM_TOOLS_OFFLINE_COOLDOWN_SEC"];
|
|
44290
|
+
const n = raw ? Number(raw) : NaN;
|
|
44291
|
+
return Number.isFinite(n) && n >= 0 ? n * 1e3 : 60 * 60 * 1e3;
|
|
44292
|
+
})();
|
|
44293
|
+
async function maybeSendTelegramToolsOfflineNotice(args) {
|
|
44294
|
+
const key2 = `${args.chatId}|tools_offline`;
|
|
44295
|
+
if (!shouldSendToolsOfflineNotice({
|
|
44296
|
+
cache: TELEGRAM_TOOLS_OFFLINE_NOTICE_CACHE,
|
|
44297
|
+
key: key2,
|
|
44298
|
+
cooldownMs: TELEGRAM_TOOLS_OFFLINE_COOLDOWN_MS
|
|
44299
|
+
})) {
|
|
44300
|
+
return;
|
|
44301
|
+
}
|
|
44302
|
+
try {
|
|
44303
|
+
const resp = await telegramApiCall(
|
|
44304
|
+
"sendMessage",
|
|
44305
|
+
{ chat_id: args.chatId, text: TOOLS_OFFLINE_USER_NOTICE },
|
|
44306
|
+
1e4
|
|
44307
|
+
);
|
|
44308
|
+
if (!resp.ok) {
|
|
44309
|
+
TELEGRAM_TOOLS_OFFLINE_NOTICE_CACHE.delete(key2);
|
|
44310
|
+
process.stderr.write(
|
|
44311
|
+
`telegram-channel(${AGENT_CODE_NAME}): tools-offline notice failed: ${resp.description ?? "unknown"}
|
|
44312
|
+
`
|
|
44313
|
+
);
|
|
44314
|
+
}
|
|
44315
|
+
} catch (err) {
|
|
44316
|
+
TELEGRAM_TOOLS_OFFLINE_NOTICE_CACHE.delete(key2);
|
|
44317
|
+
process.stderr.write(
|
|
44318
|
+
`telegram-channel(${AGENT_CODE_NAME}): tools-offline notice error: ${err.message}
|
|
44319
|
+
`
|
|
44320
|
+
);
|
|
44321
|
+
}
|
|
44322
|
+
}
|
|
43940
44323
|
async function maybeSendTelegramAccountWarn(args) {
|
|
43941
44324
|
const key2 = `${args.chatId}|account_warn`;
|
|
43942
44325
|
if (!shouldSendAccountWarn({
|
|
@@ -46176,6 +46559,10 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
46176
46559
|
]
|
|
46177
46560
|
}));
|
|
46178
46561
|
mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
46562
|
+
{
|
|
46563
|
+
const offline = refuseIfToolsOffline(req.params.name);
|
|
46564
|
+
if (offline) return offline;
|
|
46565
|
+
}
|
|
46179
46566
|
const { name, arguments: args } = req.params;
|
|
46180
46567
|
const impersonationRefusal = shouldRefuseImpersonatedEgress(
|
|
46181
46568
|
name,
|
|
@@ -46521,6 +46908,8 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
46521
46908
|
});
|
|
46522
46909
|
}
|
|
46523
46910
|
await maybeSendTelegramAccountWarn({ chatId: chat_id });
|
|
46911
|
+
void maybeSendTelegramToolsOfflineNotice({ chatId: chat_id }).catch(() => {
|
|
46912
|
+
});
|
|
46524
46913
|
return {
|
|
46525
46914
|
content: [{ type: "text", text: promiseNote ? `sent. ${promiseNote}` : "sent" }]
|
|
46526
46915
|
};
|
|
@@ -46755,72 +47144,82 @@ async function handleCallbackQuery(cb) {
|
|
|
46755
47144
|
await handlePerfReviewTap(cb, decoded.callbackId, decoded.token);
|
|
46756
47145
|
return;
|
|
46757
47146
|
}
|
|
46758
|
-
await ackCallbackQuery(cb.id, "Got it").catch(() => {
|
|
46759
|
-
});
|
|
46760
47147
|
const apiCallRuntime = await Promise.resolve().then(() => (init_ask_user_runtime(), ask_user_runtime_exports));
|
|
47148
|
+
const { answerApprovalTap: answerApprovalTap2, tappersOwnChatId: tappersOwnChatId2 } = await Promise.resolve().then(() => (init_telegram_tap_reply(), telegram_tap_reply_exports));
|
|
46761
47149
|
const cfg = {
|
|
46762
47150
|
apiHost: AGT_HOST,
|
|
46763
47151
|
apiKey: AGT_API_KEY,
|
|
46764
47152
|
agentId: AGT_AGENT_ID
|
|
46765
47153
|
};
|
|
46766
|
-
|
|
46767
|
-
|
|
46768
|
-
|
|
46769
|
-
|
|
46770
|
-
|
|
46771
|
-
|
|
46772
|
-
|
|
46773
|
-
|
|
46774
|
-
|
|
46775
|
-
|
|
46776
|
-
|
|
46777
|
-
|
|
46778
|
-
|
|
46779
|
-
|
|
46780
|
-
|
|
46781
|
-
|
|
46782
|
-
|
|
46783
|
-
|
|
46784
|
-
|
|
46785
|
-
|
|
46786
|
-
|
|
46787
|
-
|
|
46788
|
-
|
|
47154
|
+
const ownChatId = tappersOwnChatId2(cb);
|
|
47155
|
+
await answerApprovalTap2({
|
|
47156
|
+
resolve: async () => {
|
|
47157
|
+
try {
|
|
47158
|
+
const res = await apiCallRuntime.apiCall(cfg, "POST", "/host/interactive/resolve", {
|
|
47159
|
+
agent_id: cfg.agentId,
|
|
47160
|
+
callback_id: decoded.callbackId,
|
|
47161
|
+
token: decoded.token,
|
|
47162
|
+
responded_by_user: String(cb.from.id),
|
|
47163
|
+
// ENG-11372 PR3: names the chat for the API's binding shadow.
|
|
47164
|
+
surface_channel: "telegram"
|
|
47165
|
+
});
|
|
47166
|
+
const body = await res.json().catch(() => ({}));
|
|
47167
|
+
return { kind: "response", httpOk: res.ok, status: res.status, body };
|
|
47168
|
+
} catch (err) {
|
|
47169
|
+
return { kind: "threw", error: err.message };
|
|
47170
|
+
}
|
|
47171
|
+
},
|
|
47172
|
+
answer: (text, showAlert) => ackCallbackQuery(cb.id, text, { showAlert }),
|
|
47173
|
+
// Edit the original message to clear the keyboard. We only have the chat
|
|
47174
|
+
// + message coordinates when `callback_query.message` is set (Telegram
|
|
47175
|
+
// omits it for inline-mode bots, which we aren't). Empty inline_keyboard
|
|
47176
|
+
// removes the buttons but leaves the prompt text intact, so the prompt
|
|
47177
|
+
// stays scrollable history. Edit failure is non-fatal: the row is already
|
|
47178
|
+
// resolved and the agent sees the value on its next poll.
|
|
47179
|
+
clearKeyboard: async () => {
|
|
47180
|
+
if (!cb.message) return;
|
|
47181
|
+
try {
|
|
47182
|
+
const resp = await telegramApiCall(
|
|
47183
|
+
"editMessageReplyMarkup",
|
|
47184
|
+
{ chat_id: cb.message.chat.id, message_id: cb.message.message_id, reply_markup: { inline_keyboard: [] } },
|
|
47185
|
+
5e3
|
|
47186
|
+
);
|
|
47187
|
+
if (!resp.ok) {
|
|
47188
|
+
process.stderr.write(
|
|
47189
|
+
`telegram-channel(${AGENT_CODE_NAME}): editMessageReplyMarkup failed (callback_id=${decoded.callbackId}): ${resp.description ?? "unknown"}
|
|
46789
47190
|
`
|
|
46790
|
-
|
|
46791
|
-
|
|
46792
|
-
|
|
46793
|
-
return;
|
|
46794
|
-
}
|
|
46795
|
-
const confirmationLabel = "\u2713";
|
|
46796
|
-
if (cb.message) {
|
|
46797
|
-
try {
|
|
46798
|
-
const resp = await telegramApiCall(
|
|
46799
|
-
"editMessageReplyMarkup",
|
|
46800
|
-
{
|
|
46801
|
-
chat_id: cb.message.chat.id,
|
|
46802
|
-
message_id: cb.message.message_id,
|
|
46803
|
-
// Empty inline_keyboard removes the buttons but leaves the
|
|
46804
|
-
// original prompt text intact. Less destructive than
|
|
46805
|
-
// editMessageText, and the prompt remains scrollable history.
|
|
46806
|
-
reply_markup: { inline_keyboard: [] }
|
|
46807
|
-
},
|
|
46808
|
-
5e3
|
|
46809
|
-
);
|
|
46810
|
-
if (!resp.ok) {
|
|
47191
|
+
);
|
|
47192
|
+
}
|
|
47193
|
+
} catch (err) {
|
|
46811
47194
|
process.stderr.write(
|
|
46812
|
-
`telegram-channel(${AGENT_CODE_NAME}): editMessageReplyMarkup
|
|
47195
|
+
`telegram-channel(${AGENT_CODE_NAME}): editMessageReplyMarkup threw (callback_id=${decoded.callbackId}): ${err.message}
|
|
46813
47196
|
`
|
|
46814
47197
|
);
|
|
46815
47198
|
}
|
|
46816
|
-
}
|
|
46817
|
-
|
|
46818
|
-
|
|
47199
|
+
},
|
|
47200
|
+
replyPrivately: ownChatId !== null && cb.message ? async (text) => {
|
|
47201
|
+
try {
|
|
47202
|
+
const resp = await telegramApiCall(
|
|
47203
|
+
"sendMessage",
|
|
47204
|
+
{ chat_id: ownChatId, text, reply_parameters: { message_id: cb.message.message_id } },
|
|
47205
|
+
5e3
|
|
47206
|
+
);
|
|
47207
|
+
if (!resp.ok) {
|
|
47208
|
+
process.stderr.write(
|
|
47209
|
+
`telegram-channel(${AGENT_CODE_NAME}): refusal reply rejected (callback_id=${decoded.callbackId}): ${resp.description ?? "unknown"}
|
|
46819
47210
|
`
|
|
46820
|
-
|
|
46821
|
-
|
|
46822
|
-
|
|
46823
|
-
|
|
47211
|
+
);
|
|
47212
|
+
}
|
|
47213
|
+
} catch (err) {
|
|
47214
|
+
process.stderr.write(
|
|
47215
|
+
`telegram-channel(${AGENT_CODE_NAME}): refusal reply threw (callback_id=${decoded.callbackId}): ${err.message}
|
|
47216
|
+
`
|
|
47217
|
+
);
|
|
47218
|
+
}
|
|
47219
|
+
} : null,
|
|
47220
|
+
log: (line) => process.stderr.write(`telegram-channel(${AGENT_CODE_NAME}): approval tap ${line} (callback_id=${decoded.callbackId})
|
|
47221
|
+
`)
|
|
47222
|
+
});
|
|
46824
47223
|
}
|
|
46825
47224
|
var PERF_REVIEW_TOKEN_PREFIX = "pr_";
|
|
46826
47225
|
var PERF_REVIEW_COMMENT_TOKEN = "pr_comment";
|
|
@@ -46897,13 +47296,16 @@ async function postPerfReviewResolve(reviewId, token, respondedByUser, comment)
|
|
|
46897
47296
|
return false;
|
|
46898
47297
|
}
|
|
46899
47298
|
}
|
|
46900
|
-
async function ackCallbackQuery(callbackQueryId, text) {
|
|
47299
|
+
async function ackCallbackQuery(callbackQueryId, text, opts = {}) {
|
|
46901
47300
|
try {
|
|
46902
47301
|
const resp = await telegramApiCall(
|
|
46903
47302
|
"answerCallbackQuery",
|
|
46904
47303
|
{
|
|
46905
47304
|
callback_query_id: callbackQueryId,
|
|
46906
|
-
...text ? { text } : {}
|
|
47305
|
+
...text ? { text } : {},
|
|
47306
|
+
// ENG-11210: an alert is a popup the tapper dismisses, for a refusal
|
|
47307
|
+
// they need to read rather than a toast that fades.
|
|
47308
|
+
...opts.showAlert ? { show_alert: true } : {}
|
|
46907
47309
|
},
|
|
46908
47310
|
5e3
|
|
46909
47311
|
);
|
|
@@ -68,8 +68,8 @@ import {
|
|
|
68
68
|
writeDirectChatSessionState,
|
|
69
69
|
writeEgressAllowlist,
|
|
70
70
|
writePersistentClaudeWrapper
|
|
71
|
-
} from "./chunk-
|
|
72
|
-
import "./chunk-
|
|
71
|
+
} from "./chunk-JX7ZXCNZ.js";
|
|
72
|
+
import "./chunk-OBZB6MUE.js";
|
|
73
73
|
import "./chunk-OCMJ5E6J.js";
|
|
74
74
|
import "./chunk-XWVM4KPK.js";
|
|
75
75
|
import "./chunk-EL5MWGGH.js";
|
|
@@ -145,4 +145,4 @@ export {
|
|
|
145
145
|
writeEgressAllowlist,
|
|
146
146
|
writePersistentClaudeWrapper
|
|
147
147
|
};
|
|
148
|
-
//# sourceMappingURL=persistent-session-
|
|
148
|
+
//# sourceMappingURL=persistent-session-6KIQF4O7.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
paneLogPath
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-JX7ZXCNZ.js";
|
|
4
|
+
import "./chunk-OBZB6MUE.js";
|
|
5
5
|
import "./chunk-OCMJ5E6J.js";
|
|
6
6
|
import "./chunk-XWVM4KPK.js";
|
|
7
7
|
import "./chunk-EL5MWGGH.js";
|
|
@@ -802,4 +802,4 @@ export {
|
|
|
802
802
|
readAndResetSlackReplyBindingClassifications,
|
|
803
803
|
readAndResetSlackReplyTargetClassifications
|
|
804
804
|
};
|
|
805
|
-
//# sourceMappingURL=responsiveness-probe-
|
|
805
|
+
//# sourceMappingURL=responsiveness-probe-GGJNITME.js.map
|