@integrity-labs/agt-cli 0.28.1111 → 0.28.1112
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-5EYINOIW.js} +2 -2
- package/dist/{chunk-TZM6YVE7.js → chunk-NO57I56V.js} +32 -9
- package/dist/{chunk-TZM6YVE7.js.map → chunk-NO57I56V.js.map} +1 -1
- package/dist/{chunk-2UHVPGB3.js → chunk-TJUDOE53.js} +4 -4
- package/dist/{claude-pair-runtime-L7KXG3DD.js → claude-pair-runtime-CKWLQMAC.js} +2 -2
- package/dist/lib/manager-worker.js +16 -16
- package/dist/mcp/direct-chat-channel.js +259 -14
- package/dist/mcp/index.js +267 -38
- package/dist/mcp/origami.js +31 -8
- package/dist/mcp/slack-channel.js +267 -14
- package/dist/mcp/telegram-channel.js +399 -70
- package/dist/{persistent-session-HSYN3SQ2.js → persistent-session-SDYY75X7.js} +3 -3
- package/dist/{responsiveness-probe-MO4L5O6F.js → responsiveness-probe-NEBHDSMI.js} +3 -3
- package/dist/{session-auth-dead-ZKI4XCSI.js → session-auth-dead-CNV4O7RF.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-4PTXGOVO.js.map → chunk-5EYINOIW.js.map} +0 -0
- /package/dist/{chunk-2UHVPGB3.js.map → chunk-TJUDOE53.js.map} +0 -0
- /package/dist/{claude-pair-runtime-L7KXG3DD.js.map → claude-pair-runtime-CKWLQMAC.js.map} +0 -0
- /package/dist/{persistent-session-HSYN3SQ2.js.map → persistent-session-SDYY75X7.js.map} +0 -0
- /package/dist/{responsiveness-probe-MO4L5O6F.js.map → responsiveness-probe-NEBHDSMI.js.map} +0 -0
- /package/dist/{session-auth-dead-ZKI4XCSI.js.map → session-auth-dead-CNV4O7RF.js.map} +0 -0
|
@@ -30741,6 +30741,37 @@ function resolveGhostReplyMode(opts) {
|
|
|
30741
30741
|
if (cached2 !== void 0) return cached2;
|
|
30742
30742
|
return "off";
|
|
30743
30743
|
}
|
|
30744
|
+
var MAINTENANCE_FLAG_KEY = "platform-maintenance-mode";
|
|
30745
|
+
function parseMaintenanceMode(raw) {
|
|
30746
|
+
if (raw === void 0) return void 0;
|
|
30747
|
+
const v = raw.trim().toLowerCase();
|
|
30748
|
+
if (v === "") return void 0;
|
|
30749
|
+
if (v === "off" || v === "tools-offline" || v === "fleet-offline") return v;
|
|
30750
|
+
const b = envBoolean(raw);
|
|
30751
|
+
if (b === true) return "fleet-offline";
|
|
30752
|
+
if (b === false) return "off";
|
|
30753
|
+
return void 0;
|
|
30754
|
+
}
|
|
30755
|
+
function cachedMaintenanceMode(path) {
|
|
30756
|
+
try {
|
|
30757
|
+
if (!existsSync(path)) return void 0;
|
|
30758
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
30759
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
30760
|
+
const flags = parsed.flags;
|
|
30761
|
+
if (!flags || typeof flags !== "object") return void 0;
|
|
30762
|
+
const value = flags[MAINTENANCE_FLAG_KEY];
|
|
30763
|
+
if (typeof value === "string") return parseMaintenanceMode(value);
|
|
30764
|
+
if (typeof value === "boolean") return value ? "fleet-offline" : "off";
|
|
30765
|
+
return void 0;
|
|
30766
|
+
} catch {
|
|
30767
|
+
return void 0;
|
|
30768
|
+
}
|
|
30769
|
+
}
|
|
30770
|
+
function resolveMaintenanceMode(opts) {
|
|
30771
|
+
const cached2 = cachedMaintenanceMode(opts?.cachePath ?? defaultFlagsCachePath());
|
|
30772
|
+
if (cached2 !== void 0) return cached2;
|
|
30773
|
+
return "off";
|
|
30774
|
+
}
|
|
30744
30775
|
var SLACK_HOT_THREAD_FLAG_KEY = "slack-hot-thread-guard";
|
|
30745
30776
|
function parseSlackHotThreadMode(raw) {
|
|
30746
30777
|
if (raw === void 0) return void 0;
|
|
@@ -30813,17 +30844,157 @@ function resolveSlackScheduledChannelGuardMode(opts) {
|
|
|
30813
30844
|
}
|
|
30814
30845
|
|
|
30815
30846
|
// src/maintenance-mode.ts
|
|
30816
|
-
var FLAG_KEY = "platform-maintenance-mode";
|
|
30817
30847
|
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.";
|
|
30818
30848
|
function isMaintenanceModeActive(opts) {
|
|
30819
|
-
return
|
|
30820
|
-
key: FLAG_KEY,
|
|
30821
|
-
envVar: "",
|
|
30822
|
-
defaultValue: false,
|
|
30849
|
+
return resolveMaintenanceMode({
|
|
30823
30850
|
cachePath: opts?.cachePath,
|
|
30824
30851
|
env: opts?.env
|
|
30825
|
-
});
|
|
30852
|
+
}) === "fleet-offline";
|
|
30853
|
+
}
|
|
30854
|
+
|
|
30855
|
+
// src/maintenance-tools-offline.ts
|
|
30856
|
+
function isToolsOfflineActive(opts) {
|
|
30857
|
+
const mode = resolveMaintenanceMode({ cachePath: opts?.cachePath, env: opts?.env });
|
|
30858
|
+
return mode === "tools-offline" || mode === "fleet-offline";
|
|
30859
|
+
}
|
|
30860
|
+
var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
|
|
30861
|
+
// Tasks.
|
|
30862
|
+
"kanban_add",
|
|
30863
|
+
"kanban_update",
|
|
30864
|
+
"kanban_move",
|
|
30865
|
+
"kanban_done",
|
|
30866
|
+
"kanban_reopen",
|
|
30867
|
+
"kanban_assign",
|
|
30868
|
+
"kanban_reassign",
|
|
30869
|
+
"kanban_recall",
|
|
30870
|
+
"kanban_respond",
|
|
30871
|
+
"kanban_progress",
|
|
30872
|
+
"kanban_log",
|
|
30873
|
+
"kanban_assign_project",
|
|
30874
|
+
"assign_kanban_to_human",
|
|
30875
|
+
// Canvases. `dashboards_request_refresh` is gated because it enqueues
|
|
30876
|
+
// server-side work, not because it renders anything itself.
|
|
30877
|
+
"dashboards_upsert",
|
|
30878
|
+
"dashboards_persist_widget",
|
|
30879
|
+
"dashboards_request_refresh",
|
|
30880
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
30881
|
+
// FORWARDED writes (ENG-11436 / CodeRabbit on #6903). These reach the agent
|
|
30882
|
+
// through `registerForwardedApiTools()` -> `server.registerTool`, from
|
|
30883
|
+
// `packages/api/src/routes/mcp-tools.ts`, with names resolved at RUNTIME. They
|
|
30884
|
+
// are gated only because index.ts now wraps that method too; before that, this
|
|
30885
|
+
// whole block would have been a list the gate could not see.
|
|
30886
|
+
//
|
|
30887
|
+
// CodeRabbit named five (workflow_update, workflow_run_{start,phase,end},
|
|
30888
|
+
// memory_delete). Enumerating the surface to check them found 37 of the 50
|
|
30889
|
+
// forwarded tools unclassified, so the five were a subset — these are all of
|
|
30890
|
+
// the writes among them. The test now classifies that surface exhaustively, so
|
|
30891
|
+
// the next addition fails CI rather than slipping through.
|
|
30892
|
+
"composio_upload_file",
|
|
30893
|
+
"memory_delete",
|
|
30894
|
+
"memory_save",
|
|
30895
|
+
"onboarding_advance",
|
|
30896
|
+
"onboarding_provision_skills",
|
|
30897
|
+
"onboarding_provision_tasks",
|
|
30898
|
+
"onboarding_record_manager_contact",
|
|
30899
|
+
"request_batch_approval",
|
|
30900
|
+
"request_integration_access",
|
|
30901
|
+
"skill_contribute_fragment",
|
|
30902
|
+
"skill_create",
|
|
30903
|
+
"skill_delete",
|
|
30904
|
+
"skill_improve",
|
|
30905
|
+
"skill_propose_revision",
|
|
30906
|
+
"skill_publish_to_marketplace",
|
|
30907
|
+
"skill_update",
|
|
30908
|
+
"submit_performance_review",
|
|
30909
|
+
"workflow_run_end",
|
|
30910
|
+
"workflow_run_phase",
|
|
30911
|
+
"workflow_run_start",
|
|
30912
|
+
"workflow_update",
|
|
30913
|
+
// CHANNEL-SERVER write (Obi Wan `b3537c1e8495` on #6903). Dispatched by NAME in
|
|
30914
|
+
// slack-channel.ts / telegram-channel.ts / direct-chat-channel.ts, which are
|
|
30915
|
+
// separate processes that never call server.tool — so this one is enforced by
|
|
30916
|
+
// `refuseIfToolsOffline` at each dispatch point, not by the registration
|
|
30917
|
+
// wrapper. It inserts a `pending_interaction` row, the same durable
|
|
30918
|
+
// human-actionable record that gates request_buttons / request_form.
|
|
30919
|
+
"channel_request_input",
|
|
30920
|
+
// `slack.ask_user` is the SAME write under a dotted name, and it was missed
|
|
30921
|
+
// because the test's enumerator could not see dotted names at all (Obi Wan
|
|
30922
|
+
// `122ac55ff278` / `d7245eddd77f` on #6903). handleAskUser calls
|
|
30923
|
+
// runtime.createPendingInteraction -> POST /host/pending-interactions ->
|
|
30924
|
+
// inserts `pending_interaction`. Ungated, an agent on a /loop files the row,
|
|
30925
|
+
// the restore discards it, and the agent blocks forever on a resolution that
|
|
30926
|
+
// no longer exists — the exact failure the entry above is gated for.
|
|
30927
|
+
//
|
|
30928
|
+
// Verified it is the ONLY dotted channel tool that writes: createPendingInteraction
|
|
30929
|
+
// is CALLED once in the three channel servers (slack-channel.ts:8053);
|
|
30930
|
+
// slack-block-kit-runtime.ts only re-exports it.
|
|
30931
|
+
"slack.ask_user",
|
|
30932
|
+
// Workflows. FORWARDED, not local — it reaches the agent through
|
|
30933
|
+
// `server.registerTool` in registerForwardedApiTools, so it is gated only
|
|
30934
|
+
// because that method is now wrapped too (CodeRabbit on #6903).
|
|
30935
|
+
"workflow_create",
|
|
30936
|
+
// Knowledge.
|
|
30937
|
+
"knowledge_add",
|
|
30938
|
+
"knowledge_update",
|
|
30939
|
+
"knowledge_delete",
|
|
30940
|
+
// Projects and project tagging.
|
|
30941
|
+
"projects_create",
|
|
30942
|
+
"projects_update",
|
|
30943
|
+
"artefacts_assign_project",
|
|
30944
|
+
// Routines.
|
|
30945
|
+
"routine_propose",
|
|
30946
|
+
"routine_revise",
|
|
30947
|
+
"routine_retire",
|
|
30948
|
+
"submit_routine_output",
|
|
30949
|
+
// Standing rules.
|
|
30950
|
+
"rule_add",
|
|
30951
|
+
"rule_remove",
|
|
30952
|
+
// Scheduled work.
|
|
30953
|
+
"schedule_create",
|
|
30954
|
+
"schedule_update",
|
|
30955
|
+
"schedule_delete",
|
|
30956
|
+
// Requests that file a durable record a human is expected to act on. Filing
|
|
30957
|
+
// one mid-window is worse than refusing it: the record is discarded by the
|
|
30958
|
+
// restore, so the agent would have promised a human a follow-up that no
|
|
30959
|
+
// longer exists anywhere.
|
|
30960
|
+
"request_approval",
|
|
30961
|
+
"request_buttons",
|
|
30962
|
+
"request_channel_access",
|
|
30963
|
+
"request_form",
|
|
30964
|
+
"request_reconnect",
|
|
30965
|
+
"request_feature",
|
|
30966
|
+
"request_feature_withdraw",
|
|
30967
|
+
// ADR-0025 approval tools, registered by `approval-tools.ts` against the same
|
|
30968
|
+
// server object, so the wrapper in index.ts covers them too.
|
|
30969
|
+
"cancel_approval",
|
|
30970
|
+
// Status / identity.
|
|
30971
|
+
"status_standup",
|
|
30972
|
+
"status_update",
|
|
30973
|
+
"update_identity",
|
|
30974
|
+
"update_timezone",
|
|
30975
|
+
// Fleet changes.
|
|
30976
|
+
"recruit_agent",
|
|
30977
|
+
"integration_improve"
|
|
30978
|
+
]);
|
|
30979
|
+
function toolsOfflineRefusalText(toolName) {
|
|
30980
|
+
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.`;
|
|
30981
|
+
}
|
|
30982
|
+
function refuseIfToolsOffline(name, opts) {
|
|
30983
|
+
if (!MAINTENANCE_GATED_TOOLS.has(name)) return null;
|
|
30984
|
+
const isActive = opts?.isActive ?? (() => isToolsOfflineActive());
|
|
30985
|
+
if (!isActive()) return null;
|
|
30986
|
+
return { content: [{ type: "text", text: toolsOfflineRefusalText(name) }] };
|
|
30987
|
+
}
|
|
30988
|
+
function shouldSendToolsOfflineNotice(opts) {
|
|
30989
|
+
const active = opts.isActive ?? (() => isToolsOfflineActive());
|
|
30990
|
+
if (!active()) return false;
|
|
30991
|
+
const now = opts.now ?? Date.now();
|
|
30992
|
+
const last = opts.cache.get(opts.key);
|
|
30993
|
+
if (last !== void 0 && now - last < opts.cooldownMs) return false;
|
|
30994
|
+
opts.cache.set(opts.key, now);
|
|
30995
|
+
return true;
|
|
30826
30996
|
}
|
|
30997
|
+
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.";
|
|
30827
30998
|
|
|
30828
30999
|
// ../core/dist/types/agent.js
|
|
30829
31000
|
var FRAMEWORK_MODEL_SLOTS = {
|
|
@@ -39227,14 +39398,37 @@ var FLAG_REGISTRY = [
|
|
|
39227
39398
|
},
|
|
39228
39399
|
{
|
|
39229
39400
|
key: "platform-maintenance-mode",
|
|
39230
|
-
description: 'Platform-wide maintenance mode (ENG-6506)
|
|
39231
|
-
flagType: "
|
|
39232
|
-
|
|
39233
|
-
//
|
|
39234
|
-
//
|
|
39235
|
-
//
|
|
39236
|
-
//
|
|
39237
|
-
|
|
39401
|
+
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.)',
|
|
39402
|
+
flagType: "enum",
|
|
39403
|
+
allowedValues: ["off", "tools-offline", "fleet-offline"],
|
|
39404
|
+
// Declared safe value is `off` (normal operation). Fail-safe direction: a
|
|
39405
|
+
// flag-DB read error or absent flag map must never silence the fleet OR take
|
|
39406
|
+
// its write tools away on its own — agents stay fully functional unless an
|
|
39407
|
+
// operator deliberately sets a mode from the admin Feature Flags page. No
|
|
39408
|
+
// envVar: net-new control, no pre-flags env gate to migrate.
|
|
39409
|
+
//
|
|
39410
|
+
// ENG-11436 widened this from a boolean rather than adding a second flag, so
|
|
39411
|
+
// there is ONE operator control whose modes are mutually exclusive by type
|
|
39412
|
+
// instead of two similar booleans with four combinations.
|
|
39413
|
+
//
|
|
39414
|
+
// The host resolver GRANDFATHERS a boolean-shaped cache value
|
|
39415
|
+
// (true -> fleet-offline, false -> off), and the DIRECTION it protects is
|
|
39416
|
+
// narrower than an earlier version of this comment claimed (Obi Wan
|
|
39417
|
+
// `e3f0d2c154b9` on #6903). It said "an older manager still writing a boolean
|
|
39418
|
+
// must not make a central flip a silent no-op". That situation cannot arise:
|
|
39419
|
+
// once this registry change deploys, normalizeFlagValue only ever yields one
|
|
39420
|
+
// of the three strings, feature-flags-host.ts writes the heartbeat map
|
|
39421
|
+
// verbatim, so no manager writes a boolean again. What grandfathering
|
|
39422
|
+
// actually covers is a NEW MCP reading a STALE PRE-DEPLOY cache that still
|
|
39423
|
+
// holds one.
|
|
39424
|
+
//
|
|
39425
|
+
// The direction that DOES happen is not handled and cannot be: an OLD MCP
|
|
39426
|
+
// reads the new string with resolveHostBooleanFlag, gets undefined, and keeps
|
|
39427
|
+
// dispatching. That is a real regression against ENG-6506 until the fleet
|
|
39428
|
+
// converges, and it is why the runbook makes convergence a deploy-order
|
|
39429
|
+
// PRECONDITION rather than offering a boolean fallback — there is none, since
|
|
39430
|
+
// an enum flag rejects `{value: true}` with a 400.
|
|
39431
|
+
defaultValue: "off",
|
|
39238
39432
|
// Flipping this takes the ENTIRE fleet offline to end users; a mis-flip is a
|
|
39239
39433
|
// platform-wide availability incident, so mutations require explicit
|
|
39240
39434
|
// confirmation (the admin flip-reach modal).
|
|
@@ -45614,6 +45808,56 @@ var SLACK_ACCOUNT_WARN_COOLDOWN_MS = (() => {
|
|
|
45614
45808
|
const n = raw ? Number(raw) : NaN;
|
|
45615
45809
|
return Number.isFinite(n) && n >= 0 ? n * 1e3 : 24 * 60 * 60 * 1e3;
|
|
45616
45810
|
})();
|
|
45811
|
+
var SLACK_TOOLS_OFFLINE_NOTICE_CACHE = /* @__PURE__ */ new Map();
|
|
45812
|
+
var SLACK_TOOLS_OFFLINE_COOLDOWN_MS = (() => {
|
|
45813
|
+
const raw = process.env["SLACK_TOOLS_OFFLINE_COOLDOWN_SEC"];
|
|
45814
|
+
const n = raw ? Number(raw) : NaN;
|
|
45815
|
+
return Number.isFinite(n) && n >= 0 ? n * 1e3 : 60 * 60 * 1e3;
|
|
45816
|
+
})();
|
|
45817
|
+
async function maybeSendSlackToolsOfflineNotice(args) {
|
|
45818
|
+
if (!BOT_TOKEN || !args.channel) return;
|
|
45819
|
+
const key2 = `${args.channel}|${args.threadTs ?? "root"}|tools_offline`;
|
|
45820
|
+
if (!shouldSendToolsOfflineNotice({
|
|
45821
|
+
cache: SLACK_TOOLS_OFFLINE_NOTICE_CACHE,
|
|
45822
|
+
key: key2,
|
|
45823
|
+
cooldownMs: SLACK_TOOLS_OFFLINE_COOLDOWN_MS
|
|
45824
|
+
})) {
|
|
45825
|
+
return;
|
|
45826
|
+
}
|
|
45827
|
+
const controller = new AbortController();
|
|
45828
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
45829
|
+
try {
|
|
45830
|
+
const res = await fetch("https://slack.com/api/chat.postMessage", {
|
|
45831
|
+
method: "POST",
|
|
45832
|
+
signal: controller.signal,
|
|
45833
|
+
headers: {
|
|
45834
|
+
"Content-Type": "application/json",
|
|
45835
|
+
Authorization: `Bearer ${BOT_TOKEN}`
|
|
45836
|
+
},
|
|
45837
|
+
body: JSON.stringify({
|
|
45838
|
+
channel: args.channel,
|
|
45839
|
+
text: TOOLS_OFFLINE_USER_NOTICE,
|
|
45840
|
+
...args.threadTs ? { thread_ts: args.threadTs } : {}
|
|
45841
|
+
})
|
|
45842
|
+
});
|
|
45843
|
+
const data = await res.json();
|
|
45844
|
+
if (!data.ok) {
|
|
45845
|
+
SLACK_TOOLS_OFFLINE_NOTICE_CACHE.delete(key2);
|
|
45846
|
+
process.stderr.write(
|
|
45847
|
+
`slack-channel(${AGENT_CODE_NAME}): tools-offline notice post failed: ${data.error ?? "unknown"}
|
|
45848
|
+
`
|
|
45849
|
+
);
|
|
45850
|
+
}
|
|
45851
|
+
} catch (err) {
|
|
45852
|
+
SLACK_TOOLS_OFFLINE_NOTICE_CACHE.delete(key2);
|
|
45853
|
+
process.stderr.write(
|
|
45854
|
+
`slack-channel(${AGENT_CODE_NAME}): tools-offline notice error: ${err.message}
|
|
45855
|
+
`
|
|
45856
|
+
);
|
|
45857
|
+
} finally {
|
|
45858
|
+
clearTimeout(timeoutId);
|
|
45859
|
+
}
|
|
45860
|
+
}
|
|
45617
45861
|
async function maybeSendSlackAccountWarn(args) {
|
|
45618
45862
|
if (!BOT_TOKEN || !args.channel) return;
|
|
45619
45863
|
const key2 = `${args.channel}|${args.threadTs ?? "root"}|account_warn`;
|
|
@@ -48679,6 +48923,10 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
48679
48923
|
]
|
|
48680
48924
|
}));
|
|
48681
48925
|
mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
48926
|
+
{
|
|
48927
|
+
const offline = refuseIfToolsOffline(req.params.name);
|
|
48928
|
+
if (offline) return offline;
|
|
48929
|
+
}
|
|
48682
48930
|
const { name, arguments: args } = req.params;
|
|
48683
48931
|
const impersonationRefusal = shouldRefuseImpersonatedEgress(
|
|
48684
48932
|
name,
|
|
@@ -49052,6 +49300,11 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
49052
49300
|
}
|
|
49053
49301
|
const sentTs = typeof data.ts === "string" ? data.ts : void 0;
|
|
49054
49302
|
await maybeSendSlackAccountWarn({ channel: effectiveChannel, threadTs: effectiveThreadTs });
|
|
49303
|
+
void maybeSendSlackToolsOfflineNotice({
|
|
49304
|
+
channel: effectiveChannel,
|
|
49305
|
+
threadTs: effectiveThreadTs
|
|
49306
|
+
}).catch(() => {
|
|
49307
|
+
});
|
|
49055
49308
|
return {
|
|
49056
49309
|
content: [{ type: "text", text: sentTs ? `${base} (ts=${sentTs})` : base }]
|
|
49057
49310
|
};
|