instar 1.3.1639 → 1.3.1640
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/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +18 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/AppConversationStore.d.ts +39 -0
- package/dist/core/AppConversationStore.d.ts.map +1 -1
- package/dist/core/AppConversationStore.js +9 -0
- package/dist/core/AppConversationStore.js.map +1 -1
- package/dist/core/AppConversationsReplicatedStore.js +1 -1
- package/dist/core/AppConversationsReplicatedStore.js.map +1 -1
- package/dist/core/AppReplyRelayInstaller.d.ts.map +1 -1
- package/dist/core/AppReplyRelayInstaller.js +2 -0
- package/dist/core/AppReplyRelayInstaller.js.map +1 -1
- package/dist/core/CodexLifecycleProductionComposition.d.ts +1 -1
- package/dist/core/CodexLifecycleProductionComposition.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +9 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SlackReplyRelayInstaller.d.ts.map +1 -1
- package/dist/core/SlackReplyRelayInstaller.js +2 -0
- package/dist/core/SlackReplyRelayInstaller.js.map +1 -1
- package/dist/core/types.d.ts +16 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/feedback-factory/execute/ConfinedRunner.d.ts +4 -1
- package/dist/feedback-factory/execute/ConfinedRunner.d.ts.map +1 -1
- package/dist/feedback-factory/execute/ConfinedRunner.js +40 -8
- package/dist/feedback-factory/execute/ConfinedRunner.js.map +1 -1
- package/dist/feedback-factory/execute/FeedbackExecutorService.js +4 -4
- package/dist/feedback-factory/execute/FeedbackExecutorService.js.map +1 -1
- package/dist/feedback-factory/execute/executePolicy.d.ts +3 -0
- package/dist/feedback-factory/execute/executePolicy.d.ts.map +1 -1
- package/dist/feedback-factory/execute/executePolicy.js +3 -0
- package/dist/feedback-factory/execute/executePolicy.js.map +1 -1
- package/dist/messaging/messageOrigin.d.ts +94 -0
- package/dist/messaging/messageOrigin.d.ts.map +1 -0
- package/dist/messaging/messageOrigin.js +246 -0
- package/dist/messaging/messageOrigin.js.map +1 -0
- package/dist/messaging/slack/FileHandler.d.ts +3 -1
- package/dist/messaging/slack/FileHandler.d.ts.map +1 -1
- package/dist/messaging/slack/FileHandler.js +9 -1
- package/dist/messaging/slack/FileHandler.js.map +1 -1
- package/dist/messaging/slack/SlackAdapter.d.ts +9 -0
- package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
- package/dist/messaging/slack/SlackAdapter.js +60 -4
- package/dist/messaging/slack/SlackAdapter.js.map +1 -1
- package/dist/messaging/slack/SlackOriginLedger.d.ts +97 -0
- package/dist/messaging/slack/SlackOriginLedger.d.ts.map +1 -0
- package/dist/messaging/slack/SlackOriginLedger.js +298 -0
- package/dist/messaging/slack/SlackOriginLedger.js.map +1 -0
- package/dist/messaging/telegram-origin/OriginAwareness.d.ts +3 -0
- package/dist/messaging/telegram-origin/OriginAwareness.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/OriginAwareness.js +14 -0
- package/dist/messaging/telegram-origin/OriginAwareness.js.map +1 -1
- package/dist/messaging/telegram-origin/OriginStore.d.ts +5 -0
- package/dist/messaging/telegram-origin/OriginStore.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/OriginStore.js +2 -1
- package/dist/messaging/telegram-origin/OriginStore.js.map +1 -1
- package/dist/messaging/telegram-origin/OriginStore.worker.js +1 -1
- package/dist/messaging/telegram-origin/OriginStore.worker.js.map +1 -1
- package/dist/messaging/telegram-origin/OriginStoreBackend.d.ts +14 -0
- package/dist/messaging/telegram-origin/OriginStoreBackend.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/OriginStoreBackend.js +45 -0
- package/dist/messaging/telegram-origin/OriginStoreBackend.js.map +1 -1
- package/dist/messaging/telegram-origin/TelegramOriginService.d.ts +4 -0
- package/dist/messaging/telegram-origin/TelegramOriginService.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/TelegramOriginService.js +10 -2
- package/dist/messaging/telegram-origin/TelegramOriginService.js.map +1 -1
- package/dist/native/InstarWedgeCanary.app/Contents/MacOS/InstarWedgeCanary +0 -0
- package/dist/native/instar-desktop-host +0 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +3 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/mobileConversationRoutes.d.ts +17 -1
- package/dist/server/mobileConversationRoutes.d.ts.map +1 -1
- package/dist/server/mobileConversationRoutes.js +31 -6
- package/dist/server/mobileConversationRoutes.js.map +1 -1
- package/dist/server/mobileSlackRoutes.d.ts +13 -0
- package/dist/server/mobileSlackRoutes.d.ts.map +1 -1
- package/dist/server/mobileSlackRoutes.js +14 -1
- package/dist/server/mobileSlackRoutes.js.map +1 -1
- package/dist/server/originDisplayPreferences.d.ts.map +1 -1
- package/dist/server/originDisplayPreferences.js +19 -8
- package/dist/server/originDisplayPreferences.js.map +1 -1
- package/dist/server/replyOrigin.d.ts +39 -0
- package/dist/server/replyOrigin.d.ts.map +1 -0
- package/dist/server/replyOrigin.js +89 -0
- package/dist/server/replyOrigin.js.map +1 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +162 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/blocking-decision-surfaces-baseline.json +3 -3
- package/src/data/builtin-manifest.json +66 -66
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/scaffold/templates.ts +4 -1
- package/src/templates/scripts/app-reply.sh +17 -0
- package/src/templates/scripts/slack-reply.sh +23 -0
- package/upgrades/1.3.1640.md +25 -0
- package/upgrades/feedback-confined-timeout.eli16.md +7 -0
- package/upgrades/side-effects/feedback-confined-timeout.md +80 -0
- package/upgrades/side-effects/message-origin-all-platforms.md +92 -0
package/dist/server/routes.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { registerEcosystemIdeaRoutes } from './ecosystemIdeaRoutes.js';
|
|
2
|
+
import { ISSUES_TOPIC_OFFSET } from '../messaging/telegramTopicSpace.js';
|
|
3
|
+
import { SlackOriginLedger, SlackOriginRecordingError } from '../messaging/slack/SlackOriginLedger.js';
|
|
4
|
+
import { slackOriginFooter, currentAgentDisplay, deviceOriginRow, effectiveDisplay, originEvidenceFromProducer, validateDeviceOriginRow, validateMessageOriginEvidence } from '../messaging/messageOrigin.js';
|
|
5
|
+
import { resolveReplyOrigin, boundSessionName, localMachineIdentity } from './replyOrigin.js';
|
|
2
6
|
import { stripViewSignatures } from './mobilePairingRoutes.js';
|
|
3
7
|
import { parseAutoExpiryHold } from '../core/AutoExpiryLedger.js';
|
|
4
8
|
import { getCodexDeviceLimitWiring } from '../core/codexDeviceLimitWiring.js';
|
|
@@ -4347,6 +4351,14 @@ export function createRoutes(ctx) {
|
|
|
4347
4351
|
if (ctx.writeAdmission) {
|
|
4348
4352
|
base.eventLoop = ctx.writeAdmission.eventLoopStats();
|
|
4349
4353
|
}
|
|
4354
|
+
// message-origin-all-platforms §6: recording counters since boot (never message content).
|
|
4355
|
+
{
|
|
4356
|
+
const ledger = ctx.slack?.originLedger;
|
|
4357
|
+
base.messageOrigin = {
|
|
4358
|
+
app: { recorded: appOriginRecordedCount },
|
|
4359
|
+
slack: ledger ? { ...ledger.counters, coversSince: ledger.coversSince === null ? null : new Date(ledger.coversSince).toISOString() } : null,
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4350
4362
|
// Origin worker health rides the AUTHED extension: a held-sends outage is
|
|
4351
4363
|
// visible without a status fan-out, and never inferred from silence.
|
|
4352
4364
|
if (ctx.telegramOrigin) {
|
|
@@ -18754,6 +18766,71 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
18754
18766
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
18755
18767
|
}
|
|
18756
18768
|
});
|
|
18769
|
+
// message-origin-all-platforms §4/§5: the display bits for one Telegram topic (agent default, then
|
|
18770
|
+
// the topic's own override) and the batched origin join for a page of its rows.
|
|
18771
|
+
const telegramOriginDisplay = (topicId) => {
|
|
18772
|
+
let override;
|
|
18773
|
+
try {
|
|
18774
|
+
override = ctx.topicProfile?.store.resolve(String(topicId))?.messageOriginDisplay ?? undefined;
|
|
18775
|
+
}
|
|
18776
|
+
catch {
|
|
18777
|
+
override = undefined;
|
|
18778
|
+
} // @silent-fallback-ok — no readable override means the agent default
|
|
18779
|
+
return effectiveDisplay(currentAgentDisplay({ liveConfig: ctx.liveConfig, config: ctx.config }), override);
|
|
18780
|
+
};
|
|
18781
|
+
const ORIGIN_LOOKUP_BUDGET_MS = 750;
|
|
18782
|
+
const joinTelegramOrigin = async (topicId, rows, _unused) => {
|
|
18783
|
+
const display = telegramOriginDisplay(topicId);
|
|
18784
|
+
const agentRows = rows.filter(m => m.fromUser === false && (typeof m.messageId === 'number' || typeof m.messageId === 'string'));
|
|
18785
|
+
if (agentRows.length === 0)
|
|
18786
|
+
return rows.map(m => ({ ...m }));
|
|
18787
|
+
const tgConfig = ctx.config.messaging?.find(m => m.type === 'telegram')?.config;
|
|
18788
|
+
const issues = topicId >= ISSUES_TOPIC_OFFSET;
|
|
18789
|
+
const rawChat = issues ? tgConfig?.issuesChatId : (ctx.telegramOrigin?.options.bot.chatId ?? tgConfig?.chatId);
|
|
18790
|
+
const chatId = rawChat === undefined || rawChat === null || rawChat === '' ? null : String(rawChat);
|
|
18791
|
+
const store = ctx.telegramOrigin?.store;
|
|
18792
|
+
let found = null;
|
|
18793
|
+
let lookupFailure = 'lookup-failed';
|
|
18794
|
+
if (store && chatId) {
|
|
18795
|
+
try {
|
|
18796
|
+
let timer;
|
|
18797
|
+
const page = await Promise.race([
|
|
18798
|
+
store.lookupOriginEvidence({ transport: 'bot-api', chatId, messageIds: agentRows.slice(0, 100).map(m => String(m.messageId)) }),
|
|
18799
|
+
new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('origin-lookup-timeout')), ORIGIN_LOOKUP_BUDGET_MS); }),
|
|
18800
|
+
]).finally(() => { if (timer)
|
|
18801
|
+
clearTimeout(timer); });
|
|
18802
|
+
found = new Map(page.results.map(r => [r.messageId, r]));
|
|
18803
|
+
}
|
|
18804
|
+
catch (err) { // @silent-fallback-ok — rows read recorded:null with the reason, never a guess
|
|
18805
|
+
found = null;
|
|
18806
|
+
lookupFailure = err instanceof Error && err.message === 'origin-lookup-timeout' ? 'lookup-timeout' : 'lookup-failed';
|
|
18807
|
+
}
|
|
18808
|
+
}
|
|
18809
|
+
const now = Date.now();
|
|
18810
|
+
return rows.map(m => {
|
|
18811
|
+
if (m.fromUser !== false)
|
|
18812
|
+
return { ...m };
|
|
18813
|
+
const hit = found?.get(String(m.messageId));
|
|
18814
|
+
let origin;
|
|
18815
|
+
if (found === null)
|
|
18816
|
+
origin = deviceOriginRow(null, display, !(store && chatId) ? 'not-found' : lookupFailure);
|
|
18817
|
+
else if (!hit)
|
|
18818
|
+
origin = deviceOriginRow(null, display, 'not-found');
|
|
18819
|
+
else if (hit.state === 'ambiguous')
|
|
18820
|
+
origin = deviceOriginRow(null, display, 'ambiguous');
|
|
18821
|
+
else if (hit.state !== 'found' || !hit.producer)
|
|
18822
|
+
origin = deviceOriginRow(null, display, 'not-retained');
|
|
18823
|
+
else {
|
|
18824
|
+
const p = hit.producer;
|
|
18825
|
+
const evidence = p.machine && p.harness && p.model ? validateMessageOriginEvidence(originEvidenceFromProducer({
|
|
18826
|
+
producerKind: p.producerKind ?? 'session', originMachineId: String(p.originMachineId ?? ''), originMachineName: String(p.originMachineName ?? ''),
|
|
18827
|
+
harnessName: p.harnessName ?? null, machine: p.machine, harness: p.harness, model: p.model,
|
|
18828
|
+
}, now)) : null;
|
|
18829
|
+
origin = evidence ? deviceOriginRow(evidence, display) : deviceOriginRow(null, display, 'not-retained');
|
|
18830
|
+
}
|
|
18831
|
+
return { ...m, origin };
|
|
18832
|
+
});
|
|
18833
|
+
};
|
|
18757
18834
|
router.get('/telegram/topics/:topicId/messages', async (req, res) => {
|
|
18758
18835
|
if (!ctx.telegram) {
|
|
18759
18836
|
res.status(503).json({ error: 'Telegram not configured' });
|
|
@@ -18789,9 +18866,13 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
18789
18866
|
// Telegram media (mobile-native-chat-media §4.9): a device gets each recorded file as
|
|
18790
18867
|
// `attachments`, and never a server path in the text.
|
|
18791
18868
|
const joinForDevice = (rows) => (isDevice ? withTelegramMediaForDevice(ctx.appConversations ?? null, topicId, ctx.appConversations ? joinTelegramHistory(ctx.appConversations, topicId, rows, limit) : rows) : rows);
|
|
18869
|
+
// message-origin-all-platforms §4: agent rows carry `origin` for a device, and for a Bearer read
|
|
18870
|
+
// that asks (`includeOrigin=1`, set by the pool fan-out so each peer answers with its own join).
|
|
18871
|
+
const withOrigin = isDevice || req.query.includeOrigin === '1';
|
|
18792
18872
|
if (req.query.scope !== 'pool') {
|
|
18793
18873
|
const joined = joinForDevice(local);
|
|
18794
|
-
|
|
18874
|
+
const shaped = withOrigin ? await joinTelegramOrigin(topicId, joined, null) : joined;
|
|
18875
|
+
res.json({ topicId, messages: isDevice ? stripViewSignatures(shaped) : shaped });
|
|
18795
18876
|
return;
|
|
18796
18877
|
}
|
|
18797
18878
|
// Pool read (mobile-operator-chat §2.4a): every answering machine's rows, tagged with the
|
|
@@ -18807,7 +18888,7 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
18807
18888
|
failed.push({ machineId: p.machineId, error: 'peer URL refused' });
|
|
18808
18889
|
return;
|
|
18809
18890
|
}
|
|
18810
|
-
const r = await poolPeerFetch(ctx.recordRopeResult ?? undefined, p, `/telegram/topics/${topicId}/messages?limit=${limit}${beforeMs === null ? '' : `&before=${beforeMs}`}`, {
|
|
18891
|
+
const r = await poolPeerFetch(ctx.recordRopeResult ?? undefined, p, `/telegram/topics/${topicId}/messages?limit=${limit}${beforeMs === null ? '' : `&before=${beforeMs}`}${withOrigin ? '&includeOrigin=1' : ''}`, {
|
|
18811
18892
|
headers: { Authorization: `Bearer ${ctx.config.authToken}` }, signal: AbortSignal.timeout(5000), redirect: 'manual',
|
|
18812
18893
|
});
|
|
18813
18894
|
if (!r.ok) {
|
|
@@ -18834,6 +18915,28 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
18834
18915
|
}
|
|
18835
18916
|
}));
|
|
18836
18917
|
const keyOf = (m) => typeof m.eventId === 'string' && m.eventId ? `e:${m.eventId}` : `m:${m.fromUser ? 'in' : 'out'}:${m.messageId}`;
|
|
18918
|
+
if (withOrigin) {
|
|
18919
|
+
// Own rows join here; a peer row's origin is re-checked (its display bits replaced by this
|
|
18920
|
+
// machine's). On a collision a peer's recorded origin fills in one this machine cannot tell.
|
|
18921
|
+
const own = await joinTelegramOrigin(topicId, rows.filter(m => m.machine === self), null);
|
|
18922
|
+
const ownByKey = new Map(own.map(m => [keyOf(m), m]));
|
|
18923
|
+
const display = telegramOriginDisplay(topicId);
|
|
18924
|
+
for (let i = 0; i < rows.length; i++) {
|
|
18925
|
+
const m = rows[i];
|
|
18926
|
+
if (m.machine === self) {
|
|
18927
|
+
rows[i] = ownByKey.get(keyOf(m)) ?? m;
|
|
18928
|
+
continue;
|
|
18929
|
+
}
|
|
18930
|
+
if (m.fromUser !== false) {
|
|
18931
|
+
delete m.origin;
|
|
18932
|
+
continue;
|
|
18933
|
+
}
|
|
18934
|
+
m.origin = validateDeviceOriginRow(m.origin, display) ?? deviceOriginRow(null, display, 'other-machine');
|
|
18935
|
+
const mine = ownByKey.get(keyOf(m));
|
|
18936
|
+
if (mine && mine.origin?.recorded !== true && m.origin.recorded === true)
|
|
18937
|
+
mine.origin = m.origin;
|
|
18938
|
+
}
|
|
18939
|
+
}
|
|
18837
18940
|
// This machine's own rows win a key collision; a peer's row is untrusted display data.
|
|
18838
18941
|
const seen = new Set(rows.filter(m => m.machine === self).map(keyOf));
|
|
18839
18942
|
const merged = rows
|
|
@@ -19937,6 +20040,29 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
19937
20040
|
}
|
|
19938
20041
|
});
|
|
19939
20042
|
// ── Slack ──────────────────────────────────────────────────────
|
|
20043
|
+
// message-origin-all-platforms §2: the evidence ladder for app and Slack replies (no refusal here).
|
|
20044
|
+
/** App agent replies stored with their origin since boot (§6). */
|
|
20045
|
+
let appOriginRecordedCount = 0;
|
|
20046
|
+
const localMachine = localMachineIdentity(ctx.config.stateDir);
|
|
20047
|
+
const replyOriginDeps = (machineIdOverride) => ({
|
|
20048
|
+
machine: () => {
|
|
20049
|
+
const local = localMachine();
|
|
20050
|
+
const selfId = machineIdOverride ?? local.id ?? ctx.meshSelfId ?? null;
|
|
20051
|
+
const name = ctx.telegramOrigin?.options.identity.originMachineName ?? local.name
|
|
20052
|
+
?? (ctx.meshSelfId ? ctx.machinePoolRegistry?.getCapacity(ctx.meshSelfId)?.nickname ?? null : null);
|
|
20053
|
+
return { id: selfId, name: name ?? null };
|
|
20054
|
+
},
|
|
20055
|
+
originService: () => ctx.telegramOrigin?.service ?? null,
|
|
20056
|
+
runningSessionByTmux: (tmuxName) => {
|
|
20057
|
+
const s = ctx.state.listSessions({ status: 'running' }).find((x) => x.tmuxSession === tmuxName);
|
|
20058
|
+
return s ? { id: s.id, framework: s.framework ?? null, model: typeof s.model === 'string' ? s.model : null } : null;
|
|
20059
|
+
},
|
|
20060
|
+
tmuxForSessionId: (sessionId) => ctx.state.getSession(sessionId)?.tmuxSession ?? null,
|
|
20061
|
+
});
|
|
20062
|
+
const originHeader = (req) => {
|
|
20063
|
+
const raw = req.headers['x-instar-origin-session'];
|
|
20064
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
20065
|
+
};
|
|
19940
20066
|
const handleSlackReply = async (req, res) => {
|
|
19941
20067
|
if (!ctx.slack) {
|
|
19942
20068
|
res.status(503).json({ error: 'Slack not configured' });
|
|
@@ -19995,7 +20121,27 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
19995
20121
|
// `finalize-ambiguous`, NEVER re-posted), NOT the 500 catch-all below
|
|
19996
20122
|
// (500 → recovery-policy retry → double-post). The still-alive adapter
|
|
19997
20123
|
// call is abandoned; it must NOT record a delivery-id on this path.
|
|
19998
|
-
|
|
20124
|
+
// message-origin-all-platforms §2/§3: the reply's origin, scoped to exactly this one send to
|
|
20125
|
+
// this target, plus the one-line footer the agent's display bits allow.
|
|
20126
|
+
// The same channel/thread the adapter will post to (sendToChannel: options.thread_ts ?? the routing key's thread).
|
|
20127
|
+
const [scopeChannel, routedThread] = String(channelId).split(':');
|
|
20128
|
+
const effectiveThread = thread_ts ?? routedThread;
|
|
20129
|
+
const scopeThread = typeof effectiveThread === 'string' && effectiveThread ? effectiveThread : null;
|
|
20130
|
+
const replyOrigin = await resolveReplyOrigin(replyOriginDeps(), {
|
|
20131
|
+
boundSession: typeof res.locals.originBoundSession === 'string' ? res.locals.originBoundSession : null,
|
|
20132
|
+
originCredential: originHeader(req),
|
|
20133
|
+
});
|
|
20134
|
+
let originFooter = '';
|
|
20135
|
+
try {
|
|
20136
|
+
originFooter = slackOriginFooter(ctx.config.projectName ?? 'agent', replyOrigin, currentAgentDisplay({ liveConfig: ctx.liveConfig, config: ctx.config }));
|
|
20137
|
+
// The labels are outgoing content: the deterministic credential wall sees them too (§5).
|
|
20138
|
+
if (originFooter && detectOutboundCredential(originFooter).detected)
|
|
20139
|
+
originFooter = '';
|
|
20140
|
+
}
|
|
20141
|
+
catch {
|
|
20142
|
+
originFooter = '';
|
|
20143
|
+
} // @silent-fallback-ok — the footer is presentation; it never fails the send
|
|
20144
|
+
const ts = await sendWithAdapterTimeout(() => SlackOriginLedger.runInScope({ evidence: replyOrigin, channel: scopeChannel, threadTs: scopeThread }, () => ctx.slack.sendToChannel(channelId, text, { thread_ts, formatMode, ...(originFooter ? { originFooter } : {}) })), SLACK_ADAPTER_SEND_TIMEOUT_MS);
|
|
19999
20145
|
// Record the delivery-id ONLY after a successful send (spec §2.4) — a
|
|
20000
20146
|
// failed send (which throws before here, incl. the ambiguous 408
|
|
20001
20147
|
// timeout) must not poison the id so its legitimate retry isn't lost.
|
|
@@ -20044,6 +20190,12 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
20044
20190
|
res.status(408).json({ error: 'adapter-send-timeout', ambiguous: true });
|
|
20045
20191
|
return;
|
|
20046
20192
|
}
|
|
20193
|
+
// message-origin-all-platforms §3: no origin record, no send. Nothing left; say so plainly.
|
|
20194
|
+
if (err instanceof SlackOriginRecordingError) {
|
|
20195
|
+
res.status(503).json({ error: 'origin-recording-unavailable', notSent: true,
|
|
20196
|
+
detail: 'Not sent: this machine could not record which machine and model wrote the reply.' });
|
|
20197
|
+
return;
|
|
20198
|
+
}
|
|
20047
20199
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
20048
20200
|
}
|
|
20049
20201
|
};
|
|
@@ -20077,6 +20229,7 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
20077
20229
|
res.status(403).json({ error: 'conversation-bind-refused', detail: verdict.detail });
|
|
20078
20230
|
return;
|
|
20079
20231
|
}
|
|
20232
|
+
res.locals.originBoundSession = boundSessionName(verdict.boundBy);
|
|
20080
20233
|
const target = ctx.conversationRegistry?.resolve(conversationId);
|
|
20081
20234
|
if (!target || target.platform !== 'slack' || target.origin === 'replicated') {
|
|
20082
20235
|
res.status(409).json({ error: 'slack-adapter-authority-unavailable' });
|
|
@@ -20150,6 +20303,7 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
20150
20303
|
res.status(403).json({ error: 'conversation-bind-refused', detail: verdict.detail });
|
|
20151
20304
|
return;
|
|
20152
20305
|
}
|
|
20306
|
+
const appBoundSession = boundSessionName(verdict.boundBy);
|
|
20153
20307
|
const key = ctx.conversationRegistry?.appConversationFor(conversationId) ?? null;
|
|
20154
20308
|
if (!key || !app.liveConversation(key)) {
|
|
20155
20309
|
res.status(404).json({ error: 'unknown-conversation' });
|
|
@@ -20213,8 +20367,10 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
20213
20367
|
return;
|
|
20214
20368
|
}
|
|
20215
20369
|
const messageId = derivedEventId('am', key, seed);
|
|
20370
|
+
// message-origin-all-platforms §3: the origin is part of the same append as the reply.
|
|
20371
|
+
const agentOrigin = await resolveReplyOrigin(replyOriginDeps(app.deps.machineId()), { boundSession: appBoundSession, originCredential: originHeader(req) });
|
|
20216
20372
|
try {
|
|
20217
|
-
const common = { eventId, conversationKey: key, messageId, role: 'agent', text, at: new Date().toISOString(), machineId: app.deps.machineId() };
|
|
20373
|
+
const common = { eventId, conversationKey: key, messageId, role: 'agent', text, at: new Date().toISOString(), machineId: app.deps.machineId(), agentOrigin };
|
|
20218
20374
|
if (preparedFiles) {
|
|
20219
20375
|
// Publish the snapshots that were scanned, then the event: never an event without its blobs.
|
|
20220
20376
|
const attachments = preparedFiles.snapshots.map((snap, index) => {
|
|
@@ -20227,11 +20383,13 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
|
|
|
20227
20383
|
};
|
|
20228
20384
|
});
|
|
20229
20385
|
app.store.append({ ...common, kind: 'media-message', attachments });
|
|
20386
|
+
appOriginRecordedCount++;
|
|
20230
20387
|
for (const id of preparedFiles.reservationIds)
|
|
20231
20388
|
app.media.releaseReply(id);
|
|
20232
20389
|
}
|
|
20233
20390
|
else {
|
|
20234
20391
|
app.store.append({ ...common, kind: 'message' });
|
|
20392
|
+
appOriginRecordedCount++;
|
|
20235
20393
|
}
|
|
20236
20394
|
}
|
|
20237
20395
|
catch (err) {
|