@integrity-labs/agt-cli 0.28.1052 → 0.28.1054
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-MQ4COFCI.js → chunk-2SGXGNDW.js} +11 -1
- package/dist/{chunk-MQ4COFCI.js.map → chunk-2SGXGNDW.js.map} +1 -1
- package/dist/{chunk-4ZH75WMF.js → chunk-AT7E625H.js} +2 -2
- package/dist/{chunk-3E63PEXT.js → chunk-M4ADTK6F.js} +8 -4
- package/dist/{chunk-3E63PEXT.js.map → chunk-M4ADTK6F.js.map} +1 -1
- package/dist/{claude-pair-runtime-KMPRE2ZJ.js → claude-pair-runtime-GO3C7KT6.js} +2 -2
- package/dist/lib/manager-worker.js +78 -14
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +10 -0
- package/dist/mcp/index.js +39 -0
- package/dist/mcp/origami.js +10 -0
- package/dist/mcp/slack-channel.js +159 -22
- package/dist/mcp/telegram-channel.js +10 -0
- package/dist/{persistent-session-5RX4P7GP.js → persistent-session-3WRSJIDM.js} +3 -3
- package/dist/{responsiveness-probe-C75AUK6I.js → responsiveness-probe-2DF6VH7Q.js} +3 -3
- package/dist/{session-auth-dead-VFRUHVTF.js → session-auth-dead-GRTHA2Z5.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-4ZH75WMF.js.map → chunk-AT7E625H.js.map} +0 -0
- /package/dist/{claude-pair-runtime-KMPRE2ZJ.js.map → claude-pair-runtime-GO3C7KT6.js.map} +0 -0
- /package/dist/{persistent-session-5RX4P7GP.js.map → persistent-session-3WRSJIDM.js.map} +0 -0
- /package/dist/{responsiveness-probe-C75AUK6I.js.map → responsiveness-probe-2DF6VH7Q.js.map} +0 -0
- /package/dist/{session-auth-dead-VFRUHVTF.js.map → session-auth-dead-GRTHA2Z5.js.map} +0 -0
|
@@ -37900,6 +37900,16 @@ var FLAG_REGISTRY = [
|
|
|
37900
37900
|
// put a first-of-its-kind classifier in front of real customer traffic with nothing
|
|
37901
37901
|
// observed. Arm per-org, then revisit once there is evidence.
|
|
37902
37902
|
},
|
|
37903
|
+
{
|
|
37904
|
+
key: "heartbeat-writes-claude-account-email",
|
|
37905
|
+
description: "ENG-10796 (#6382): when ON, the host heartbeat writes the Claude account address it reads from the host (oauthAccount.emailAddress) into hosts.claude_account_email, so the Tokens label self-heals. OFF (default) the heartbeat never writes the column and the label stays whatever an operator set. Ships DARK because the column must first sit behind column-level grants (ENG-11162, #6596): until then any org member, and every co-tenant org on a pool host, can read it raw through Supabase REST, and on an IL-provisioned box the harvested address is a staff login (Obi Wan ecad15b6eff5). Evaluated at PLATFORM scope only: the heartbeat does not know the host org before its UPDATE, so flip the platform value once ENG-11162 is live.",
|
|
37906
|
+
flagType: "boolean",
|
|
37907
|
+
// Safe value is `false` (write nothing). A flag read error degrades here, so a flags outage
|
|
37908
|
+
// can never start harvesting addresses into a customer-readable row.
|
|
37909
|
+
defaultValue: false,
|
|
37910
|
+
// Turning it on starts publishing an identifier into customer-visible rows (ADR-0022 §4).
|
|
37911
|
+
sensitive: true
|
|
37912
|
+
},
|
|
37903
37913
|
{
|
|
37904
37914
|
key: "email-guard-require-approval-enabled",
|
|
37905
37915
|
description: "Platform enable-gate for the email-domain guardrail REQUIRE_APPROVAL stage (ENG-7939). When ON for an org, a scope at email_guard_stage=require_approval routes a send to a non-allowed domain into Human Approval (an Approve/Deny card) instead of a hard block: the rollout UI unlocks the option and the write routes accept require_approval. When OFF (default) the write routes reject require_approval and the UI shows it as not yet available, so no scope can select it. Structurally also depends on hitl-approval-routing being on (the approval broker fails closed to a hard block when routing is off). Evaluated per-org. Ships dark.",
|
package/dist/mcp/index.js
CHANGED
|
@@ -30231,6 +30231,16 @@ var FLAG_REGISTRY = [
|
|
|
30231
30231
|
// put a first-of-its-kind classifier in front of real customer traffic with nothing
|
|
30232
30232
|
// observed. Arm per-org, then revisit once there is evidence.
|
|
30233
30233
|
},
|
|
30234
|
+
{
|
|
30235
|
+
key: "heartbeat-writes-claude-account-email",
|
|
30236
|
+
description: "ENG-10796 (#6382): when ON, the host heartbeat writes the Claude account address it reads from the host (oauthAccount.emailAddress) into hosts.claude_account_email, so the Tokens label self-heals. OFF (default) the heartbeat never writes the column and the label stays whatever an operator set. Ships DARK because the column must first sit behind column-level grants (ENG-11162, #6596): until then any org member, and every co-tenant org on a pool host, can read it raw through Supabase REST, and on an IL-provisioned box the harvested address is a staff login (Obi Wan ecad15b6eff5). Evaluated at PLATFORM scope only: the heartbeat does not know the host org before its UPDATE, so flip the platform value once ENG-11162 is live.",
|
|
30237
|
+
flagType: "boolean",
|
|
30238
|
+
// Safe value is `false` (write nothing). A flag read error degrades here, so a flags outage
|
|
30239
|
+
// can never start harvesting addresses into a customer-readable row.
|
|
30240
|
+
defaultValue: false,
|
|
30241
|
+
// Turning it on starts publishing an identifier into customer-visible rows (ADR-0022 §4).
|
|
30242
|
+
sensitive: true
|
|
30243
|
+
},
|
|
30234
30244
|
{
|
|
30235
30245
|
key: "email-guard-require-approval-enabled",
|
|
30236
30246
|
description: "Platform enable-gate for the email-domain guardrail REQUIRE_APPROVAL stage (ENG-7939). When ON for an org, a scope at email_guard_stage=require_approval routes a send to a non-allowed domain into Human Approval (an Approve/Deny card) instead of a hard block: the rollout UI unlocks the option and the write routes accept require_approval. When OFF (default) the write routes reject require_approval and the UI shows it as not yet available, so no scope can select it. Structurally also depends on hitl-approval-routing being on (the approval broker fails closed to a hard block when routing is off). Evaluated per-org. Ships dark.",
|
|
@@ -50585,6 +50595,8 @@ var LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
50585
50595
|
"projects_list",
|
|
50586
50596
|
"projects_get",
|
|
50587
50597
|
"projects_create",
|
|
50598
|
+
// CS-1900: rename / archive / Linear link on an existing project.
|
|
50599
|
+
"projects_update",
|
|
50588
50600
|
"artefacts_assign_project",
|
|
50589
50601
|
"kanban_assign_project",
|
|
50590
50602
|
// ENG-6229: self-restart. Conditionally registered (dark unless
|
|
@@ -51122,6 +51134,33 @@ server.tool(
|
|
|
51122
51134
|
content: [{ type: "text", text: await createProjectText(params) }]
|
|
51123
51135
|
})
|
|
51124
51136
|
);
|
|
51137
|
+
async function updateProjectText(params) {
|
|
51138
|
+
const data = await apiPost(
|
|
51139
|
+
`/host/projects/${encodeURIComponent(params.project_id)}/update`,
|
|
51140
|
+
{
|
|
51141
|
+
agent_id: AGT_AGENT_ID,
|
|
51142
|
+
name: params.name,
|
|
51143
|
+
status: params.status,
|
|
51144
|
+
linear_project_id: params.linear_project_id
|
|
51145
|
+
}
|
|
51146
|
+
);
|
|
51147
|
+
const p2 = data.project;
|
|
51148
|
+
return `Updated project "${p2.name}" [${p2.status}] (id: ${p2.id}).`;
|
|
51149
|
+
}
|
|
51150
|
+
server.tool(
|
|
51151
|
+
"projects_update",
|
|
51152
|
+
'Rename a project, archive or unarchive it, or set/clear its Linear link. Pass only the fields to change. A project is shared by your whole team, so ask the human to confirm before renaming or archiving one, naming the project and the change (e.g. archive "Theoretical Store Profitability"). Archiving hides it from active lists but keeps its tasks and artefacts; set status "active" to undo. Projects have no cover image, so there is nothing to set for one.',
|
|
51153
|
+
{
|
|
51154
|
+
project_id: external_exports.string().uuid().describe("The project id returned by projects_list"),
|
|
51155
|
+
// Trim BEFORE the length check, as the host does (CodeRabbit on #6642).
|
|
51156
|
+
name: external_exports.string().trim().min(1).max(200).optional().describe("New project name"),
|
|
51157
|
+
status: external_exports.enum(["active", "archived"]).optional().describe('"archived" to archive, "active" to unarchive'),
|
|
51158
|
+
linear_project_id: external_exports.string().nullable().optional().describe("Linear project id to deep-link, or null to clear the link")
|
|
51159
|
+
},
|
|
51160
|
+
async (params) => ({
|
|
51161
|
+
content: [{ type: "text", text: await updateProjectText(params) }]
|
|
51162
|
+
})
|
|
51163
|
+
);
|
|
51125
51164
|
async function assignArtefactProjectText(params) {
|
|
51126
51165
|
const data = await apiPost(
|
|
51127
51166
|
`/host/artefact/${encodeURIComponent(params.artefact_id)}/assign-project`,
|
package/dist/mcp/origami.js
CHANGED
|
@@ -44145,6 +44145,16 @@ var FLAG_REGISTRY = [
|
|
|
44145
44145
|
// put a first-of-its-kind classifier in front of real customer traffic with nothing
|
|
44146
44146
|
// observed. Arm per-org, then revisit once there is evidence.
|
|
44147
44147
|
},
|
|
44148
|
+
{
|
|
44149
|
+
key: "heartbeat-writes-claude-account-email",
|
|
44150
|
+
description: "ENG-10796 (#6382): when ON, the host heartbeat writes the Claude account address it reads from the host (oauthAccount.emailAddress) into hosts.claude_account_email, so the Tokens label self-heals. OFF (default) the heartbeat never writes the column and the label stays whatever an operator set. Ships DARK because the column must first sit behind column-level grants (ENG-11162, #6596): until then any org member, and every co-tenant org on a pool host, can read it raw through Supabase REST, and on an IL-provisioned box the harvested address is a staff login (Obi Wan ecad15b6eff5). Evaluated at PLATFORM scope only: the heartbeat does not know the host org before its UPDATE, so flip the platform value once ENG-11162 is live.",
|
|
44151
|
+
flagType: "boolean",
|
|
44152
|
+
// Safe value is `false` (write nothing). A flag read error degrades here, so a flags outage
|
|
44153
|
+
// can never start harvesting addresses into a customer-readable row.
|
|
44154
|
+
defaultValue: false,
|
|
44155
|
+
// Turning it on starts publishing an identifier into customer-visible rows (ADR-0022 §4).
|
|
44156
|
+
sensitive: true
|
|
44157
|
+
},
|
|
44148
44158
|
{
|
|
44149
44159
|
key: "email-guard-require-approval-enabled",
|
|
44150
44160
|
description: "Platform enable-gate for the email-domain guardrail REQUIRE_APPROVAL stage (ENG-7939). When ON for an org, a scope at email_guard_stage=require_approval routes a send to a non-allowed domain into Human Approval (an Approve/Deny card) instead of a hard block: the rollout UI unlocks the option and the write routes accept require_approval. When OFF (default) the write routes reject require_approval and the UI shows it as not yet available, so no scope can select it. Structurally also depends on hitl-approval-routing being on (the approval broker fails closed to a hard block when routing is off). Evaluated per-org. Ships dark.",
|
|
@@ -38784,6 +38784,16 @@ var FLAG_REGISTRY = [
|
|
|
38784
38784
|
// put a first-of-its-kind classifier in front of real customer traffic with nothing
|
|
38785
38785
|
// observed. Arm per-org, then revisit once there is evidence.
|
|
38786
38786
|
},
|
|
38787
|
+
{
|
|
38788
|
+
key: "heartbeat-writes-claude-account-email",
|
|
38789
|
+
description: "ENG-10796 (#6382): when ON, the host heartbeat writes the Claude account address it reads from the host (oauthAccount.emailAddress) into hosts.claude_account_email, so the Tokens label self-heals. OFF (default) the heartbeat never writes the column and the label stays whatever an operator set. Ships DARK because the column must first sit behind column-level grants (ENG-11162, #6596): until then any org member, and every co-tenant org on a pool host, can read it raw through Supabase REST, and on an IL-provisioned box the harvested address is a staff login (Obi Wan ecad15b6eff5). Evaluated at PLATFORM scope only: the heartbeat does not know the host org before its UPDATE, so flip the platform value once ENG-11162 is live.",
|
|
38790
|
+
flagType: "boolean",
|
|
38791
|
+
// Safe value is `false` (write nothing). A flag read error degrades here, so a flags outage
|
|
38792
|
+
// can never start harvesting addresses into a customer-readable row.
|
|
38793
|
+
defaultValue: false,
|
|
38794
|
+
// Turning it on starts publishing an identifier into customer-visible rows (ADR-0022 §4).
|
|
38795
|
+
sensitive: true
|
|
38796
|
+
},
|
|
38787
38797
|
{
|
|
38788
38798
|
key: "email-guard-require-approval-enabled",
|
|
38789
38799
|
description: "Platform enable-gate for the email-domain guardrail REQUIRE_APPROVAL stage (ENG-7939). When ON for an org, a scope at email_guard_stage=require_approval routes a send to a non-allowed domain into Human Approval (an Approve/Deny card) instead of a hard block: the rollout UI unlocks the option and the write routes accept require_approval. When OFF (default) the write routes reject require_approval and the UI shows it as not yet available, so no scope can select it. Structurally also depends on hitl-approval-routing being on (the approval broker fails closed to a hard block when routing is off). Evaluated per-org. Ships dark.",
|
|
@@ -41181,11 +41191,24 @@ function buildIngressThreadKey(channel, threadTs) {
|
|
|
41181
41191
|
}
|
|
41182
41192
|
function createIngressOnlyThreadRegistry(maxEntries = 500) {
|
|
41183
41193
|
const marked = /* @__PURE__ */ new Set();
|
|
41194
|
+
const cleared = /* @__PURE__ */ new Set();
|
|
41195
|
+
const remember = (set, key2) => {
|
|
41196
|
+
set.delete(key2);
|
|
41197
|
+
set.add(key2);
|
|
41198
|
+
while (set.size > maxEntries) {
|
|
41199
|
+
const oldest = set.values().next();
|
|
41200
|
+
if (oldest.done)
|
|
41201
|
+
break;
|
|
41202
|
+
set.delete(oldest.value);
|
|
41203
|
+
}
|
|
41204
|
+
};
|
|
41184
41205
|
return {
|
|
41185
41206
|
mark(channel, threadTs) {
|
|
41186
41207
|
const key2 = buildIngressThreadKey(channel, threadTs);
|
|
41187
41208
|
if (!key2)
|
|
41188
41209
|
return;
|
|
41210
|
+
if (cleared.has(key2))
|
|
41211
|
+
return;
|
|
41189
41212
|
marked.delete(key2);
|
|
41190
41213
|
marked.add(key2);
|
|
41191
41214
|
while (marked.size > maxEntries) {
|
|
@@ -41200,6 +41223,7 @@ function createIngressOnlyThreadRegistry(maxEntries = 500) {
|
|
|
41200
41223
|
if (!key2)
|
|
41201
41224
|
return;
|
|
41202
41225
|
marked.delete(key2);
|
|
41226
|
+
remember(cleared, key2);
|
|
41203
41227
|
},
|
|
41204
41228
|
isIngressOnly(channel, threadTs) {
|
|
41205
41229
|
const key2 = buildIngressThreadKey(channel, threadTs);
|
|
@@ -41212,6 +41236,16 @@ function createIngressOnlyThreadRegistry(maxEntries = 500) {
|
|
|
41212
41236
|
}
|
|
41213
41237
|
};
|
|
41214
41238
|
}
|
|
41239
|
+
function shouldSuppressDirectPost(input) {
|
|
41240
|
+
if (input.persistedHumanOwed === true)
|
|
41241
|
+
return false;
|
|
41242
|
+
if (input.persistedIngressOnly === true)
|
|
41243
|
+
return true;
|
|
41244
|
+
return input.ingressOnly.isIngressOnly(input.channel, input.threadTs);
|
|
41245
|
+
}
|
|
41246
|
+
function threadMarkersAllIngressOnly(markers) {
|
|
41247
|
+
return markers.length > 0 && markers.every((m) => m.ingress_only === true);
|
|
41248
|
+
}
|
|
41215
41249
|
var INGRESS_ONLY_REFUSAL = "This thread was opened by a third-party bot (an alert or issue card), which can wake you but is not someone you can talk to - replying would start a bot-to-bot exchange in a shared channel. Act on the content instead: work the issue, or raise it with a person in a thread they are in. If a human posts here, this thread becomes repliable.";
|
|
41216
41250
|
|
|
41217
41251
|
// ../core/dist/channels/governance/mention-echo-twin-audit.js
|
|
@@ -44020,6 +44054,24 @@ function channelMessageShouldWake(text, responseMode, notifyMode, channelId, mut
|
|
|
44020
44054
|
}
|
|
44021
44055
|
return channelMessageShouldRespond(text, responseMode);
|
|
44022
44056
|
}
|
|
44057
|
+
function decideTopLevelChannelWake(input) {
|
|
44058
|
+
const muted = Boolean(input.channelId && input.mutedChannels?.has(input.channelId));
|
|
44059
|
+
if (input.thirdPartyBotGranted) {
|
|
44060
|
+
return muted ? { wake: false, reason: "muted" } : { wake: true, via: "third_party_bot" };
|
|
44061
|
+
}
|
|
44062
|
+
if (channelMessageShouldWake(
|
|
44063
|
+
input.text,
|
|
44064
|
+
input.responseMode,
|
|
44065
|
+
input.notifyMode,
|
|
44066
|
+
input.channelId,
|
|
44067
|
+
input.mutedChannels,
|
|
44068
|
+
input.agent
|
|
44069
|
+
)) {
|
|
44070
|
+
return { wake: true, via: "engagement" };
|
|
44071
|
+
}
|
|
44072
|
+
const muteApplies = input.notifyMode === "filter" || input.notifyMode === "addressed";
|
|
44073
|
+
return { wake: false, reason: muted && muteApplies ? "muted" : "not_engaged" };
|
|
44074
|
+
}
|
|
44023
44075
|
function parseNotifyCommand(text, currentChannelId) {
|
|
44024
44076
|
const arg = (text ?? "").trim().toLowerCase().split(/\s+/)[0] ?? "";
|
|
44025
44077
|
if (arg === "off" || arg === "mute") return { action: "mute", channelId: currentChannelId };
|
|
@@ -44035,7 +44087,12 @@ function notifyStatusMessage(muted, mode) {
|
|
|
44035
44087
|
return muted ? ":mute: This channel is *muted* \u2014 I won\u2019t wake on its messages, including ones that name me (a direct @-mention still reaches me). `/notify on` to resume." : ":bell: This channel is *active* \u2014 I wake on messages that address me by name, not on every message here (an @-mention always reaches me). `/notify off` to mute.";
|
|
44036
44088
|
}
|
|
44037
44089
|
const unblock = "This is the `notify-dispatch` flag: an operator turns it on, and a session already running only picks it up after a restart \u2014 so if it has just been enabled, ask for me to be restarted.";
|
|
44038
|
-
return muted ? `:grey_question: Your *mute* for this channel is
|
|
44090
|
+
return muted ? `:grey_question: Your *mute* for this channel is saved and *only partly in effect*: ${NOTIFY_BOT_MUTE_CLAUSE} Everything else here is unchanged, because I\u2019m not in a per-channel dispatch mode right now; it applies to those as soon as one is live. ${unblock}` : `:grey_question: *No mute saved* for this channel. I\u2019m not in a per-channel dispatch mode right now, so a mute would not change what other messages do here, but \`/notify off\` would still stop any allowlisted third-party bot (an alert or issue integration such as Linear) waking me here straight away. ${unblock}`;
|
|
44091
|
+
}
|
|
44092
|
+
var NOTIFY_BOT_MUTE_CLAUSE = "it already stops any allowlisted third-party bot (an alert or issue integration such as Linear) from waking me here.";
|
|
44093
|
+
function notifyAckNote(mode) {
|
|
44094
|
+
if (mode === "filter" || mode === "addressed") return "";
|
|
44095
|
+
return ` _(note: I\u2019m not in a per-channel dispatch mode right now. A mute here ${NOTIFY_BOT_MUTE_CLAUSE.replace(/^it /, "")} Other messages are unaffected until an operator enables per-channel dispatch.)_`;
|
|
44039
44096
|
}
|
|
44040
44097
|
|
|
44041
44098
|
// src/slack-list-channels.ts
|
|
@@ -45445,7 +45502,8 @@ function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undelivera
|
|
|
45445
45502
|
...discretionary ? { discretionary: true } : {},
|
|
45446
45503
|
// ENG-5969/ENG-6327: carry the replay payload only when provided (the
|
|
45447
45504
|
// durable-replay path). Absent ⇒ the marker can't be replayed.
|
|
45448
|
-
...payload ? { payload } : {}
|
|
45505
|
+
...payload ? { payload } : {},
|
|
45506
|
+
...ingressOnlyThreads.isIngressOnly(channel, threadTs) ? { ingress_only: true } : {}
|
|
45449
45507
|
};
|
|
45450
45508
|
try {
|
|
45451
45509
|
mkdirSync9(SLACK_PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
|
|
@@ -45589,6 +45647,18 @@ function slackConversationReplayFacts(channel, threadTs) {
|
|
|
45589
45647
|
}
|
|
45590
45648
|
function sendUndeliverableNotice(channel, threadTs, conversationKey) {
|
|
45591
45649
|
if (!BOT_TOKEN || !channel) return;
|
|
45650
|
+
if (shouldSuppressDirectPost({
|
|
45651
|
+
channel,
|
|
45652
|
+
threadTs,
|
|
45653
|
+
ingressOnly: ingressOnlyThreads,
|
|
45654
|
+
persistedHumanOwed: readSlackThreadMarkerState(channel, threadTs).humanOwed
|
|
45655
|
+
})) {
|
|
45656
|
+
process.stderr.write(
|
|
45657
|
+
`slack-channel(${AGENT_CODE_NAME}): undeliverable notice suppressed reason=ingress_only thread=${redactSlackId(threadTs)}
|
|
45658
|
+
`
|
|
45659
|
+
);
|
|
45660
|
+
return;
|
|
45661
|
+
}
|
|
45592
45662
|
const text = undeliverableNoticeTextForRepeat({
|
|
45593
45663
|
replay: slackConversationReplayFacts(channel, threadTs) ?? void 0,
|
|
45594
45664
|
priorNotices: slackUndeliverableNotices.noticeCount(conversationKey)
|
|
@@ -45641,6 +45711,18 @@ function deleteSlackUndeliverableNotice(conversationKey) {
|
|
|
45641
45711
|
var lastSlackBusyAckNoticeAt = /* @__PURE__ */ new Map();
|
|
45642
45712
|
function postBusyAckNotice(channel, threadTs, isThreadReply, state) {
|
|
45643
45713
|
if (!BOT_TOKEN || !channel) return;
|
|
45714
|
+
if (shouldSuppressDirectPost({
|
|
45715
|
+
channel,
|
|
45716
|
+
threadTs,
|
|
45717
|
+
ingressOnly: ingressOnlyThreads,
|
|
45718
|
+
persistedHumanOwed: readSlackThreadMarkerState(channel, threadTs).humanOwed
|
|
45719
|
+
})) {
|
|
45720
|
+
process.stderr.write(
|
|
45721
|
+
`slack-channel(${AGENT_CODE_NAME}): busy-ack suppressed reason=ingress_only thread=${redactSlackId(threadTs)}
|
|
45722
|
+
`
|
|
45723
|
+
);
|
|
45724
|
+
return;
|
|
45725
|
+
}
|
|
45644
45726
|
const now = Date.now();
|
|
45645
45727
|
const conversationKey = isThreadReply && threadTs ? `${channel}:${threadTs}` : channel;
|
|
45646
45728
|
if (!shouldPostUndeliverableNotice(
|
|
@@ -45834,6 +45916,32 @@ function slackNextRetryName(filename) {
|
|
|
45834
45916
|
}
|
|
45835
45917
|
return { next: `${base}.retry-${attempt}.json`, attempt };
|
|
45836
45918
|
}
|
|
45919
|
+
function readSlackThreadMarkerState(channel, threadTs) {
|
|
45920
|
+
const none = { allIngressOnly: false, humanOwed: false };
|
|
45921
|
+
if (!threadTs || !SLACK_PENDING_INBOUND_DIR) return none;
|
|
45922
|
+
const markers = [];
|
|
45923
|
+
try {
|
|
45924
|
+
markSeenAllSlackPendingMarkersForThread(
|
|
45925
|
+
SLACK_PENDING_INBOUND_DIR,
|
|
45926
|
+
channel,
|
|
45927
|
+
threadTs,
|
|
45928
|
+
(fullPath) => {
|
|
45929
|
+
try {
|
|
45930
|
+
const m = JSON.parse(readFileSync18(fullPath, "utf-8"));
|
|
45931
|
+
markers.push({ ingress_only: m.ingress_only });
|
|
45932
|
+
} catch {
|
|
45933
|
+
markers.push({});
|
|
45934
|
+
}
|
|
45935
|
+
}
|
|
45936
|
+
);
|
|
45937
|
+
} catch {
|
|
45938
|
+
return none;
|
|
45939
|
+
}
|
|
45940
|
+
return {
|
|
45941
|
+
allIngressOnly: threadMarkersAllIngressOnly(markers),
|
|
45942
|
+
humanOwed: markers.some((m) => m.ingress_only !== true)
|
|
45943
|
+
};
|
|
45944
|
+
}
|
|
45837
45945
|
async function processSlackRecoveryOutboxFile(filename) {
|
|
45838
45946
|
if (!SLACK_RECOVERY_OUTBOX_DIR) return;
|
|
45839
45947
|
if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
|
|
@@ -45877,6 +45985,26 @@ async function processSlackRecoveryOutboxFile(filename) {
|
|
|
45877
45985
|
const urlFixed = stripUrlWrappingAsterisks(mrkdwn.text);
|
|
45878
45986
|
if (urlFixed.stripped) emitUrlAsteriskStripTelemetry("slack", urlFixed.count);
|
|
45879
45987
|
const text = urlFixed.text;
|
|
45988
|
+
const recoveryMarkers = readSlackThreadMarkerState(payload.channel, payload.thread_ts);
|
|
45989
|
+
if (shouldSuppressDirectPost({
|
|
45990
|
+
channel: payload.channel,
|
|
45991
|
+
threadTs: payload.thread_ts,
|
|
45992
|
+
ingressOnly: ingressOnlyThreads,
|
|
45993
|
+
persistedIngressOnly: recoveryMarkers.allIngressOnly,
|
|
45994
|
+
persistedHumanOwed: recoveryMarkers.humanOwed
|
|
45995
|
+
})) {
|
|
45996
|
+
process.stderr.write(
|
|
45997
|
+
`slack-channel(${AGENT_CODE_NAME}): ghost-reply recovery suppressed reason=ingress_only channel=${redactSlackId(payload.channel)} thread=${redactSlackId(payload.thread_ts)}
|
|
45998
|
+
`
|
|
45999
|
+
);
|
|
46000
|
+
if (payload.thread_ts) clearPendingMessage(payload.channel, payload.thread_ts);
|
|
46001
|
+
removeRecoveryLedgerEntry(SLACK_RECOVERY_LEDGER_DIR, payload.marker_name);
|
|
46002
|
+
try {
|
|
46003
|
+
unlinkSync7(fullPath);
|
|
46004
|
+
} catch {
|
|
46005
|
+
}
|
|
46006
|
+
return;
|
|
46007
|
+
}
|
|
45880
46008
|
const ghostReplyMode = resolveGhostReplyMode();
|
|
45881
46009
|
if (ghostReplyMode !== "off") {
|
|
45882
46010
|
const decision = await decideRecovery(text, ghostReplyMode);
|
|
@@ -47119,8 +47247,7 @@ async function handleNotifyCommand(payload, codeName) {
|
|
|
47119
47247
|
);
|
|
47120
47248
|
return;
|
|
47121
47249
|
}
|
|
47122
|
-
const
|
|
47123
|
-
const inactiveNote = muteIsLive ? "" : " _(note: I\u2019m not in a per-channel dispatch mode right now, so this is saved but won\u2019t take effect until an operator enables it.)_";
|
|
47250
|
+
const inactiveNote = notifyAckNote(NOTIFY_DISPATCH_MODE);
|
|
47124
47251
|
const { action } = parseNotifyCommand(payload.text, channelId);
|
|
47125
47252
|
if (action === "help") {
|
|
47126
47253
|
await postEphemeralViaResponseUrl(
|
|
@@ -49801,15 +49928,13 @@ function connectSocketModeSafely() {
|
|
|
49801
49928
|
void startSocketModeAfterSeedingMutes();
|
|
49802
49929
|
}
|
|
49803
49930
|
async function startSocketModeAfterSeedingMutes() {
|
|
49804
|
-
|
|
49805
|
-
|
|
49806
|
-
|
|
49807
|
-
|
|
49808
|
-
|
|
49809
|
-
`slack-channel: notify-prefs seed threw, connecting anyway: ${err.message}
|
|
49931
|
+
try {
|
|
49932
|
+
await seedMutedChannelsFromApi();
|
|
49933
|
+
} catch (err) {
|
|
49934
|
+
process.stderr.write(
|
|
49935
|
+
`slack-channel: notify-prefs seed threw, connecting anyway: ${err.message}
|
|
49810
49936
|
`
|
|
49811
|
-
|
|
49812
|
-
}
|
|
49937
|
+
);
|
|
49813
49938
|
}
|
|
49814
49939
|
if (isShuttingDown) return;
|
|
49815
49940
|
void runOrRetry(connectSocketMode, {
|
|
@@ -50074,8 +50199,12 @@ async function connectSocketMode() {
|
|
|
50074
50199
|
if (passedIdentity) recordActivity("inbound");
|
|
50075
50200
|
const ingressThreadTs = evt.thread_ts ?? evt.ts;
|
|
50076
50201
|
if (peerClassification?.kind === "bot-ingress" && access.kind !== "drop") {
|
|
50077
|
-
|
|
50078
|
-
|
|
50202
|
+
if (evt.channel && readSlackThreadMarkerState(evt.channel, ingressThreadTs).humanOwed) {
|
|
50203
|
+
ingressOnlyThreads.clear(evt.channel, ingressThreadTs);
|
|
50204
|
+
} else {
|
|
50205
|
+
ingressOnlyThreads.mark(evt.channel, ingressThreadTs);
|
|
50206
|
+
}
|
|
50207
|
+
} else if (access.kind !== "drop" && (!isBot || peerClassification?.kind === "peer-ingress")) {
|
|
50079
50208
|
ingressOnlyThreads.clear(evt.channel, ingressThreadTs);
|
|
50080
50209
|
}
|
|
50081
50210
|
if (access.kind === "drop") {
|
|
@@ -50277,17 +50406,25 @@ async function connectSocketMode() {
|
|
|
50277
50406
|
return;
|
|
50278
50407
|
}
|
|
50279
50408
|
} else {
|
|
50280
|
-
|
|
50281
|
-
evt.text ?? "",
|
|
50282
|
-
CHANNEL_RESPONSE_MODE,
|
|
50283
|
-
NOTIFY_DISPATCH_MODE,
|
|
50284
|
-
evt.channel,
|
|
50285
|
-
MUTED_CHANNELS,
|
|
50409
|
+
const wake = decideTopLevelChannelWake({
|
|
50410
|
+
text: evt.text ?? "",
|
|
50411
|
+
responseMode: CHANNEL_RESPONSE_MODE,
|
|
50412
|
+
notifyMode: NOTIFY_DISPATCH_MODE,
|
|
50413
|
+
channelId: evt.channel,
|
|
50414
|
+
mutedChannels: MUTED_CHANNELS,
|
|
50286
50415
|
// ENG-8657: who this agent is, so `addressed` can tell a message
|
|
50287
50416
|
// naming IT from one naming a different agent in the same channel.
|
|
50288
|
-
{ botUserId, codeName: AGENT_CODE_NAME }
|
|
50289
|
-
|
|
50417
|
+
agent: { botUserId, codeName: AGENT_CODE_NAME },
|
|
50418
|
+
thirdPartyBotGranted: peerClassification?.kind === "bot-ingress"
|
|
50419
|
+
});
|
|
50420
|
+
if (!wake.wake) {
|
|
50421
|
+
const channelHash = createHash3("sha256").update(evt.channel ?? "").digest("hex").slice(0, 8);
|
|
50422
|
+
process.stderr.write(
|
|
50423
|
+
`slack-channel: inbound drop reason=engagement:${wake.reason} notify=${NOTIFY_DISPATCH_MODE} response=${CHANNEL_RESPONSE_MODE} bot=${isBot} channel=${channelHash} ts=${redactSlackId(evt.ts)}
|
|
50424
|
+
`
|
|
50425
|
+
);
|
|
50290
50426
|
return;
|
|
50427
|
+
}
|
|
50291
50428
|
}
|
|
50292
50429
|
}
|
|
50293
50430
|
const composed = composeInboundText(evt);
|
|
@@ -38890,6 +38890,16 @@ var FLAG_REGISTRY = [
|
|
|
38890
38890
|
// put a first-of-its-kind classifier in front of real customer traffic with nothing
|
|
38891
38891
|
// observed. Arm per-org, then revisit once there is evidence.
|
|
38892
38892
|
},
|
|
38893
|
+
{
|
|
38894
|
+
key: "heartbeat-writes-claude-account-email",
|
|
38895
|
+
description: "ENG-10796 (#6382): when ON, the host heartbeat writes the Claude account address it reads from the host (oauthAccount.emailAddress) into hosts.claude_account_email, so the Tokens label self-heals. OFF (default) the heartbeat never writes the column and the label stays whatever an operator set. Ships DARK because the column must first sit behind column-level grants (ENG-11162, #6596): until then any org member, and every co-tenant org on a pool host, can read it raw through Supabase REST, and on an IL-provisioned box the harvested address is a staff login (Obi Wan ecad15b6eff5). Evaluated at PLATFORM scope only: the heartbeat does not know the host org before its UPDATE, so flip the platform value once ENG-11162 is live.",
|
|
38896
|
+
flagType: "boolean",
|
|
38897
|
+
// Safe value is `false` (write nothing). A flag read error degrades here, so a flags outage
|
|
38898
|
+
// can never start harvesting addresses into a customer-readable row.
|
|
38899
|
+
defaultValue: false,
|
|
38900
|
+
// Turning it on starts publishing an identifier into customer-visible rows (ADR-0022 §4).
|
|
38901
|
+
sensitive: true
|
|
38902
|
+
},
|
|
38893
38903
|
{
|
|
38894
38904
|
key: "email-guard-require-approval-enabled",
|
|
38895
38905
|
description: "Platform enable-gate for the email-domain guardrail REQUIRE_APPROVAL stage (ENG-7939). When ON for an org, a scope at email_guard_stage=require_approval routes a send to a non-allowed domain into Human Approval (an Approve/Deny card) instead of a hard block: the rollout UI unlocks the option and the write routes accept require_approval. When OFF (default) the write routes reject require_approval and the UI shows it as not yet available, so no scope can select it. Structurally also depends on hitl-approval-routing being on (the approval broker fails closed to a hard block when routing is off). Evaluated per-org. Ships dark.",
|
|
@@ -65,8 +65,8 @@ import {
|
|
|
65
65
|
writeDirectChatSessionState,
|
|
66
66
|
writeEgressAllowlist,
|
|
67
67
|
writePersistentClaudeWrapper
|
|
68
|
-
} from "./chunk-
|
|
69
|
-
import "./chunk-
|
|
68
|
+
} from "./chunk-AT7E625H.js";
|
|
69
|
+
import "./chunk-2SGXGNDW.js";
|
|
70
70
|
import "./chunk-OCMJ5E6J.js";
|
|
71
71
|
import "./chunk-XWVM4KPK.js";
|
|
72
72
|
import "./chunk-EL5MWGGH.js";
|
|
@@ -139,4 +139,4 @@ export {
|
|
|
139
139
|
writeEgressAllowlist,
|
|
140
140
|
writePersistentClaudeWrapper
|
|
141
141
|
};
|
|
142
|
-
//# sourceMappingURL=persistent-session-
|
|
142
|
+
//# sourceMappingURL=persistent-session-3WRSJIDM.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
paneLogPath
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-AT7E625H.js";
|
|
4
|
+
import "./chunk-2SGXGNDW.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-2DF6VH7Q.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sessionTranscriptDir
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2SGXGNDW.js";
|
|
4
4
|
import "./chunk-DGUM43GV.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/session-auth-dead.ts
|
|
@@ -204,4 +204,4 @@ export {
|
|
|
204
204
|
decideSessionAuthState,
|
|
205
205
|
probeSessionAuth
|
|
206
206
|
};
|
|
207
|
-
//# sourceMappingURL=session-auth-dead-
|
|
207
|
+
//# sourceMappingURL=session-auth-dead-GRTHA2Z5.js.map
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|