negotium 0.7.1 → 0.9.0
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/README.md +3 -4
- package/dist/agent-helpers.js +17 -8
- package/dist/agent-helpers.js.map +10 -9
- package/dist/background-bash.js.map +2 -2
- package/dist/browser-runtime.js.map +2 -2
- package/dist/{chunk-4y98yvq8.js → chunk-ae0ya01v.js} +2 -2
- package/dist/{chunk-4y98yvq8.js.map → chunk-ae0ya01v.js.map} +2 -2
- package/dist/chunk-pp6dvrzq.js.map +2 -2
- package/dist/hosted-agent.js +2 -2
- package/dist/hosted-agent.js.map +4 -4
- package/dist/main.js +946 -227
- package/dist/main.js.map +25 -24
- package/dist/mcp-factories.js +17 -8
- package/dist/mcp-factories.js.map +10 -9
- package/dist/media.js.map +2 -2
- package/dist/prompts.js.map +2 -2
- package/dist/query-runtime.js +7 -2
- package/dist/query-runtime.js.map +7 -6
- package/dist/registry.js +1 -1
- package/dist/runtime/src/application/submit-runtime-gateway-turn.ts +10 -6
- package/dist/runtime/src/identifiers.ts +49 -0
- package/dist/runtime/src/index.ts +22 -0
- package/dist/runtime/src/node-host.ts +7 -0
- package/dist/runtime/src/query/index.ts +0 -1
- package/dist/runtime/src/query/public-runtime.ts +1 -5
- package/dist/runtime/src/query/room-query-registry.ts +4 -2
- package/dist/runtime/src/query/types.ts +0 -76
- package/dist/runtime/src/runtime/turn-runner.ts +4 -2
- package/dist/runtime/src/runtime-gateway.ts +19 -0
- package/dist/runtime/src/storage/api-topics.ts +32 -12
- package/dist/runtime/src/storage/storage-public.ts +4 -39
- package/dist/runtime/src/topics/personal-general.ts +3 -3
- package/dist/runtime/src/types.ts +0 -11
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-gateway.js +6 -2
- package/dist/runtime-gateway.js.map +3 -3
- package/dist/runtime-helpers.js.map +2 -2
- package/dist/storage.js +11 -35
- package/dist/storage.js.map +4 -4
- package/dist/types/packages/core/src/identifiers.d.ts +27 -0
- package/dist/types/packages/core/src/query/public-runtime.d.ts +1 -1
- package/dist/types/packages/core/src/query/room-query-registry.d.ts +2 -1
- package/dist/types/packages/core/src/query/types.d.ts +0 -59
- package/dist/types/packages/core/src/runtime-gateway.d.ts +5 -0
- package/dist/types/packages/core/src/storage/api-topics.d.ts +10 -4
- package/dist/types/packages/core/src/storage/storage-public.d.ts +2 -3
- package/dist/types/packages/core/src/types.d.ts +0 -10
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js.map +2 -2
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -3708,7 +3708,7 @@ var exports_version = {};
|
|
|
3708
3708
|
__export(exports_version, {
|
|
3709
3709
|
NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
|
|
3710
3710
|
});
|
|
3711
|
-
var NEGOTIUM_VERSION = "0.
|
|
3711
|
+
var NEGOTIUM_VERSION = "0.9.0";
|
|
3712
3712
|
|
|
3713
3713
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3714
3714
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -7884,6 +7884,7 @@ __export(exports_api_topics, {
|
|
|
7884
7884
|
upsertTopic: () => upsertTopic,
|
|
7885
7885
|
stampUnscopedOtiumTopics: () => stampUnscopedOtiumTopics,
|
|
7886
7886
|
setTopicSurfaces: () => setTopicSurfaces,
|
|
7887
|
+
setTopicSurfaceScope: () => setTopicSurfaceScope,
|
|
7887
7888
|
setTopicSessionId: () => setTopicSessionId,
|
|
7888
7889
|
setSurfaceScopeRequired: () => setSurfaceScopeRequired,
|
|
7889
7890
|
setDefaultSurfaceScope: () => setDefaultSurfaceScope,
|
|
@@ -8412,11 +8413,9 @@ function normalizedTitle(title) {
|
|
|
8412
8413
|
return title.trim().toLowerCase();
|
|
8413
8414
|
}
|
|
8414
8415
|
function surfaceScopeForWrite(t) {
|
|
8415
|
-
if (normalizeTopicSurface(t.surface ?? defaultTopicSurface()) !== "otium")
|
|
8416
|
-
return null;
|
|
8417
8416
|
if (t.surfaceScope !== undefined)
|
|
8418
8417
|
return normalizeSurfaceScope(t.surfaceScope);
|
|
8419
|
-
return defaultSurfaceScope();
|
|
8418
|
+
return normalizeTopicSurface(t.surface ?? defaultTopicSurface()) === "otium" ? defaultSurfaceScope() : null;
|
|
8420
8419
|
}
|
|
8421
8420
|
function upsertTopic(t) {
|
|
8422
8421
|
const normalized = normalizeTopicState({
|
|
@@ -8561,6 +8560,13 @@ function setTopicSurfaces(topicIds, surface) {
|
|
|
8561
8560
|
})();
|
|
8562
8561
|
return changed;
|
|
8563
8562
|
}
|
|
8563
|
+
function setTopicSurfaceScope(topicId, surface, surfaceScope) {
|
|
8564
|
+
const normalizedSurface = normalizeTopicSurface(surface);
|
|
8565
|
+
const normalizedScope = normalizeSurfaceScope(surfaceScope);
|
|
8566
|
+
const result = db.query(`UPDATE api_topics SET surface = ?, surface_scope = ?
|
|
8567
|
+
WHERE id = ? AND (surface IS NOT ? OR surface_scope IS NOT ?)`).run(normalizedSurface, normalizedScope, topicId, normalizedSurface, normalizedScope);
|
|
8568
|
+
return Number(result.changes ?? 0) > 0;
|
|
8569
|
+
}
|
|
8564
8570
|
function getTopicByNameForUser(title, userId, lookupScope) {
|
|
8565
8571
|
const opts = "scope" in lookupScope ? {} : lookupScope;
|
|
8566
8572
|
const trimmed = title.trim();
|
|
@@ -11511,9 +11517,32 @@ var init_manager2 = __esm(async () => {
|
|
|
11511
11517
|
}, 30 * 60 * 1000).unref();
|
|
11512
11518
|
});
|
|
11513
11519
|
|
|
11520
|
+
// ../../packages/core/src/identifiers.ts
|
|
11521
|
+
function asUserId(value) {
|
|
11522
|
+
return value;
|
|
11523
|
+
}
|
|
11524
|
+
function asTopicId(value) {
|
|
11525
|
+
return value;
|
|
11526
|
+
}
|
|
11527
|
+
function asMessageId(value) {
|
|
11528
|
+
return value;
|
|
11529
|
+
}
|
|
11530
|
+
function asRequestId(value) {
|
|
11531
|
+
return value;
|
|
11532
|
+
}
|
|
11533
|
+
function asClientMessageId(value) {
|
|
11534
|
+
return value;
|
|
11535
|
+
}
|
|
11536
|
+
function asTurnId(value) {
|
|
11537
|
+
return value;
|
|
11538
|
+
}
|
|
11539
|
+
function asTurnSlotKey(value) {
|
|
11540
|
+
return value;
|
|
11541
|
+
}
|
|
11542
|
+
|
|
11514
11543
|
// ../../packages/core/src/query/room-query-registry.ts
|
|
11515
11544
|
function isolatedTurnRoomId(topicId, queryId) {
|
|
11516
|
-
return `${topicId}${ISOLATED_TURN_ROOM_MARKER}${queryId}
|
|
11545
|
+
return asTurnSlotKey(`${topicId}${ISOLATED_TURN_ROOM_MARKER}${queryId}`);
|
|
11517
11546
|
}
|
|
11518
11547
|
function isIsolatedTurnRoomId(roomId) {
|
|
11519
11548
|
return roomId.includes(ISOLATED_TURN_ROOM_MARKER);
|
|
@@ -11642,6 +11671,7 @@ function createRoomQueryRegistry(host) {
|
|
|
11642
11671
|
};
|
|
11643
11672
|
}
|
|
11644
11673
|
var ISOLATED_TURN_ROOM_MARKER = "::isolated-turn::";
|
|
11674
|
+
var init_room_query_registry = () => {};
|
|
11645
11675
|
|
|
11646
11676
|
// ../../packages/core/src/query/types.ts
|
|
11647
11677
|
var AbortReason;
|
|
@@ -11867,7 +11897,9 @@ var roomQueryRegistry, interSessionQueue;
|
|
|
11867
11897
|
var init_active_rooms = __esm(async () => {
|
|
11868
11898
|
init_logger();
|
|
11869
11899
|
await init_runtime_leases();
|
|
11900
|
+
init_room_query_registry();
|
|
11870
11901
|
init_types2();
|
|
11902
|
+
init_room_query_registry();
|
|
11871
11903
|
roomQueryRegistry = createRoomQueryRegistry({
|
|
11872
11904
|
instanceId: RUNTIME_INSTANCE_ID,
|
|
11873
11905
|
internalAbortReason: "internal" /* Internal */,
|
|
@@ -19713,8 +19745,8 @@ function startAiTurn(params) {
|
|
|
19713
19745
|
const askReplySources = params.askReplySources;
|
|
19714
19746
|
const sessionRetried = params._sessionRetried === true;
|
|
19715
19747
|
const emptyResponseRetried = params._emptyResponseRetried === true;
|
|
19716
|
-
const queryId = params._queryId ?? randomUUID17();
|
|
19717
|
-
const roomId = turnConcurrency === "isolated" ? isolatedTurnRoomId(topicId, queryId) : topicId;
|
|
19748
|
+
const queryId = asTurnId(params._queryId ?? randomUUID17());
|
|
19749
|
+
const roomId = turnConcurrency === "isolated" ? isolatedTurnRoomId(topicId, queryId) : asTurnSlotKey(topicId);
|
|
19718
19750
|
const currentRuntimeEpoch = getRuntimeTopicEpoch(topic.id);
|
|
19719
19751
|
const runtimeEpoch = params._runtimeEpoch ?? currentRuntimeEpoch;
|
|
19720
19752
|
if (params._runtimeEpoch !== undefined && params._runtimeEpoch !== currentRuntimeEpoch) {
|
|
@@ -21518,12 +21550,13 @@ var init_runtime_gateway_submissions = __esm(async () => {
|
|
|
21518
21550
|
// ../../packages/core/src/application/submit-runtime-gateway-turn.ts
|
|
21519
21551
|
import { createHash as createHash9, randomUUID as randomUUID18 } from "crypto";
|
|
21520
21552
|
function duplicateResult(submission, params, requestId, actorUserId, payloadHash) {
|
|
21553
|
+
const sourceAdapter = params.sourceAdapter?.trim() || "runtime-gateway";
|
|
21521
21554
|
const message = getApiMessage(submission.topicId, submission.messageId) ?? (params.silent ? {
|
|
21522
21555
|
id: submission.messageId,
|
|
21523
21556
|
topicId: params.topic.id,
|
|
21524
21557
|
authorId: actorUserId,
|
|
21525
21558
|
authorName: params.actorLabel,
|
|
21526
|
-
sourceAdapter
|
|
21559
|
+
sourceAdapter,
|
|
21527
21560
|
sourceMessageId: params.clientMessageId,
|
|
21528
21561
|
text: params.text,
|
|
21529
21562
|
createdAt: submission.createdAt
|
|
@@ -21566,6 +21599,7 @@ function gatewayPayloadHash(params, requestId, actorUserId) {
|
|
|
21566
21599
|
function submitRuntimeGatewayTurn(params) {
|
|
21567
21600
|
const requestId = params.requestId ?? params.clientMessageId;
|
|
21568
21601
|
const actorUserId = params.actorUserId ?? params.userId;
|
|
21602
|
+
const sourceAdapter = params.sourceAdapter?.trim() || "runtime-gateway";
|
|
21569
21603
|
const respond = params.respond ?? true;
|
|
21570
21604
|
const payloadHash = gatewayPayloadHash(params, requestId, actorUserId);
|
|
21571
21605
|
const existing = findRuntimeGatewaySubmission(params.clientMessageId, requestId);
|
|
@@ -21586,7 +21620,7 @@ function submitRuntimeGatewayTurn(params) {
|
|
|
21586
21620
|
topicId: params.topic.id,
|
|
21587
21621
|
authorId: actorUserId,
|
|
21588
21622
|
authorName: params.actorLabel,
|
|
21589
|
-
sourceAdapter
|
|
21623
|
+
sourceAdapter,
|
|
21590
21624
|
sourceMessageId: params.clientMessageId,
|
|
21591
21625
|
text: params.text,
|
|
21592
21626
|
...attachments2?.length ? { attachments: attachments2 } : {},
|
|
@@ -24134,6 +24168,7 @@ __export(exports_src, {
|
|
|
24134
24168
|
startAiTurn: () => startAiTurn,
|
|
24135
24169
|
stampUnscopedOtiumTopics: () => stampUnscopedOtiumTopics,
|
|
24136
24170
|
setTopicSurfaces: () => setTopicSurfaces,
|
|
24171
|
+
setTopicSurfaceScope: () => setTopicSurfaceScope,
|
|
24137
24172
|
setTopicSessionId: () => setTopicSessionId,
|
|
24138
24173
|
setSurfaceScopeRequired: () => setSurfaceScopeRequired,
|
|
24139
24174
|
setRuntimeMcpPort: () => setRuntimeMcpPort,
|
|
@@ -24295,6 +24330,13 @@ __export(exports_src, {
|
|
|
24295
24330
|
bgBashContextCapability: () => bgBashContextCapability,
|
|
24296
24331
|
backgroundSessionProgress: () => backgroundSessionProgress,
|
|
24297
24332
|
attachmentPromptLine: () => attachmentPromptLine,
|
|
24333
|
+
asUserId: () => asUserId,
|
|
24334
|
+
asTurnSlotKey: () => asTurnSlotKey,
|
|
24335
|
+
asTurnId: () => asTurnId,
|
|
24336
|
+
asTopicId: () => asTopicId,
|
|
24337
|
+
asRequestId: () => asRequestId,
|
|
24338
|
+
asMessageId: () => asMessageId,
|
|
24339
|
+
asClientMessageId: () => asClientMessageId,
|
|
24298
24340
|
appendJsonlEntry: () => appendJsonlEntry,
|
|
24299
24341
|
appendApiMessage: () => appendApiMessage,
|
|
24300
24342
|
answerPendingAskUserQuestion: () => answerPendingAskUserQuestion,
|
|
@@ -24793,6 +24835,11 @@ __export(exports_node_host, {
|
|
|
24793
24835
|
earliestRuntimeEventSeq: () => earliestRuntimeEventSeq,
|
|
24794
24836
|
deleteVaultEntry: () => deleteVaultEntry,
|
|
24795
24837
|
compactTopicSession: () => compactTopicSession,
|
|
24838
|
+
asUserId: () => asUserId,
|
|
24839
|
+
asTopicId: () => asTopicId,
|
|
24840
|
+
asRequestId: () => asRequestId,
|
|
24841
|
+
asMessageId: () => asMessageId,
|
|
24842
|
+
asClientMessageId: () => asClientMessageId,
|
|
24796
24843
|
appendApiMessage: () => appendApiMessage,
|
|
24797
24844
|
acquireRuntimeProcessLease: () => acquireRuntimeProcessLease,
|
|
24798
24845
|
abortAllRooms: () => abortAllRooms,
|
|
@@ -29407,7 +29454,6 @@ __export(exports_storage_public, {
|
|
|
29407
29454
|
waitForRequiredRuntimeProcessLease: () => waitForRequiredRuntimeProcessLease,
|
|
29408
29455
|
validateDecisionGraph: () => validateDecisionGraph,
|
|
29409
29456
|
upsertTopic: () => upsertTopic,
|
|
29410
|
-
updateTopicThreadId: () => updateTopicThreadId,
|
|
29411
29457
|
updateTasks: () => updateTasks,
|
|
29412
29458
|
updateDecisions: () => updateDecisions,
|
|
29413
29459
|
updateApiMessageUsage: () => updateApiMessageUsage,
|
|
@@ -29429,21 +29475,12 @@ __export(exports_storage_public, {
|
|
|
29429
29475
|
softDeleteApiMessage: () => softDeleteApiMessage,
|
|
29430
29476
|
settleTopicArchiveJob: () => settleTopicArchiveJob,
|
|
29431
29477
|
setTopicSurfaces: () => setTopicSurfaces,
|
|
29478
|
+
setTopicSurfaceScope: () => setTopicSurfaceScope,
|
|
29432
29479
|
setTopicSessionId: () => setTopicSessionId,
|
|
29433
|
-
setTopicMcpExtra: () => setTopicMcpExtra,
|
|
29434
|
-
setTopicMcpEnabled: () => setTopicMcpEnabled,
|
|
29435
|
-
setTopicForkOrigin: () => setTopicForkOrigin,
|
|
29436
|
-
setTopicDescription: () => setTopicDescription,
|
|
29437
29480
|
setTopicBrief: () => setTopicBrief,
|
|
29438
29481
|
setTopicArchiveState: () => setTopicArchiveState,
|
|
29439
|
-
setTopicAgentAndSession: () => setTopicAgentAndSession,
|
|
29440
|
-
setTopicAgentAndClearSession: () => setTopicAgentAndClearSession,
|
|
29441
|
-
setTopicAgent: () => setTopicAgent,
|
|
29442
29482
|
setSurfaceScopeRequired: () => setSurfaceScopeRequired,
|
|
29443
|
-
setSessionForTopic: () => setSessionForTopic,
|
|
29444
|
-
setLastShownConfig: () => setLastShownConfig,
|
|
29445
29483
|
setGlobalAiName: () => setGlobalAiName,
|
|
29446
|
-
setDmSessionId: () => setDmSessionId,
|
|
29447
29484
|
setDefaultSurfaceScope: () => setDefaultSurfaceScope,
|
|
29448
29485
|
setApiTopicConfig: () => setApiTopicConfig,
|
|
29449
29486
|
setApiTopicAgent: () => setApiTopicAgent,
|
|
@@ -29459,8 +29496,6 @@ __export(exports_storage_public, {
|
|
|
29459
29496
|
reparentTopicChildren: () => reparentTopicChildren,
|
|
29460
29497
|
renderTaskList: () => renderTaskList,
|
|
29461
29498
|
renderDecisionList: () => renderDecisionList,
|
|
29462
|
-
renameTopic: () => renameTopic,
|
|
29463
|
-
removeTopic: () => removeTopic,
|
|
29464
29499
|
removeSentFilesForTopic: () => removeSentFilesForTopic,
|
|
29465
29500
|
removeParticipantFromDB: () => removeParticipantFromDB,
|
|
29466
29501
|
removeDeadRuntimeProcessLeases: () => removeDeadRuntimeProcessLeases,
|
|
@@ -29503,49 +29538,34 @@ __export(exports_storage_public, {
|
|
|
29503
29538
|
hasActiveConversation: () => hasActiveConversation,
|
|
29504
29539
|
grantSubagentTellTarget: () => grantSubagentTellTarget,
|
|
29505
29540
|
getWikiDir: () => getWikiDir,
|
|
29506
|
-
getUserConfig: () => getUserConfig,
|
|
29507
29541
|
getTopicStats: () => getTopicStats,
|
|
29508
29542
|
getTopicSessionId: () => getTopicSessionId,
|
|
29509
|
-
getTopicNames: () => getTopicNames,
|
|
29510
29543
|
getTopicMemoryOrigin: () => getTopicMemoryOrigin,
|
|
29511
29544
|
getTopicMemoryFilePaths: () => getTopicMemoryFilePaths,
|
|
29512
|
-
getTopicMcpConfig: () => getTopicMcpConfig,
|
|
29513
|
-
getTopicDescription: () => getTopicDescription,
|
|
29514
29545
|
getTopicByNameForUser: () => getTopicByNameForUser,
|
|
29515
29546
|
getTopicByNameAndKind: () => getTopicByNameAndKind,
|
|
29516
29547
|
getTopicByName: () => getTopicByName,
|
|
29517
29548
|
getTopicBrief: () => getTopicBrief,
|
|
29518
29549
|
getTopicArchiveState: () => getTopicArchiveState,
|
|
29519
|
-
getTopicAgentSwitchState: () => getTopicAgentSwitchState,
|
|
29520
|
-
getTopicAgent: () => getTopicAgent,
|
|
29521
29550
|
getTopic: () => getTopic,
|
|
29522
29551
|
getThreadSummaries: () => getThreadSummaries,
|
|
29523
29552
|
getTaskFilePath: () => getTaskFilePath,
|
|
29524
29553
|
getStats: () => getStats,
|
|
29525
29554
|
getSharedWikiDir: () => getSharedWikiDir,
|
|
29526
|
-
getSessionForTopic: () => getSessionForTopic,
|
|
29527
29555
|
getRuntimeProcessLease: () => getRuntimeProcessLease,
|
|
29528
29556
|
getMessagesForTopicAfterRowid: () => getMessagesForTopicAfterRowid,
|
|
29529
29557
|
getManagerTopicForUser: () => getManagerTopicForUser,
|
|
29530
|
-
getLastShownConfig: () => getLastShownConfig,
|
|
29531
29558
|
getLastMessagePreviews: () => getLastMessagePreviews,
|
|
29532
29559
|
getGlobalAiName: () => getGlobalAiName,
|
|
29533
|
-
getForumTopicByName: () => getTopicByName2,
|
|
29534
|
-
getDmSessionId: () => getDmSessionId,
|
|
29535
29560
|
getDecisionGraphSvgPath: () => getDecisionGraphSvgPath,
|
|
29536
29561
|
getDecisionFilePath: () => getDecisionFilePath,
|
|
29537
29562
|
getConversationPath: () => getConversationPath,
|
|
29538
|
-
getCommunicateThreadId: () => getCommunicateThreadId,
|
|
29539
29563
|
getApiTopicConfig: () => getApiTopicConfig,
|
|
29540
29564
|
getApiMessage: () => getApiMessage,
|
|
29541
|
-
getAllUserIds: () => getAllUserIds,
|
|
29542
|
-
getAllTopics: () => getAllTopics,
|
|
29543
29565
|
getAllMessagesForTopic: () => getAllMessagesForTopic,
|
|
29544
29566
|
getActiveConversationPath: () => getActiveConversationPath,
|
|
29545
29567
|
forum: () => exports_forum,
|
|
29546
29568
|
formatTopicArchiveTranscriptRecord: () => formatTopicArchiveTranscriptRecord,
|
|
29547
|
-
flushSessionCache: () => flushSessionCache,
|
|
29548
|
-
findUserByThread: () => findUserByThread,
|
|
29549
29569
|
findTopicTitleConflict: () => findTopicTitleConflict,
|
|
29550
29570
|
findRecentUserMessage: () => findRecentUserMessage,
|
|
29551
29571
|
findLastSessionIdForAgent: () => findLastSessionIdForAgent,
|
|
@@ -29573,10 +29593,8 @@ __export(exports_storage_public, {
|
|
|
29573
29593
|
configureStorageHost: () => configureStorageHost2,
|
|
29574
29594
|
cloneConversationLog: () => cloneConversationLog,
|
|
29575
29595
|
clearTopicSessionId: () => clearTopicSessionId,
|
|
29576
|
-
clearSessionForTopic: () => clearSessionForTopic,
|
|
29577
29596
|
clearPendingAskSources: () => clearPendingAskSources,
|
|
29578
29597
|
clearPendingAsk: () => clearPendingAsk,
|
|
29579
|
-
clearDmSessionId: () => clearDmSessionId,
|
|
29580
29598
|
claimTopicArchiveJob: () => claimTopicArchiveJob,
|
|
29581
29599
|
claimAskUserGateAndSelect: () => claimAskUserGateAndSelect,
|
|
29582
29600
|
captureMessageSnapshotForTopic: () => captureMessageSnapshotForTopic,
|
|
@@ -29596,7 +29614,6 @@ __export(exports_storage_public, {
|
|
|
29596
29614
|
apiMessages: () => exports_api_messages,
|
|
29597
29615
|
anonymizeApiMessages: () => anonymizeApiMessages,
|
|
29598
29616
|
aiMentionFromMode: () => aiMentionFromMode,
|
|
29599
|
-
addTopic: () => addTopic,
|
|
29600
29617
|
addParticipantToDB: () => addParticipantToDB,
|
|
29601
29618
|
activityLog: () => exports_activity_log,
|
|
29602
29619
|
acquireRuntimeProcessLease: () => acquireRuntimeProcessLease,
|
|
@@ -29628,7 +29645,6 @@ var init_storage_public = __esm(async () => {
|
|
|
29628
29645
|
await init_decisions();
|
|
29629
29646
|
init_decisions();
|
|
29630
29647
|
await init_forum();
|
|
29631
|
-
await init_forum();
|
|
29632
29648
|
await init_runtime_process_leases();
|
|
29633
29649
|
init_runtime_process_leases();
|
|
29634
29650
|
await init_session_asks();
|
|
@@ -29643,7 +29659,6 @@ var init_storage_public = __esm(async () => {
|
|
|
29643
29659
|
await init_topic_archive_state();
|
|
29644
29660
|
init_topic_archive_state();
|
|
29645
29661
|
await init_topic_settings();
|
|
29646
|
-
init_topic_settings();
|
|
29647
29662
|
await init_wiki();
|
|
29648
29663
|
init_wiki();
|
|
29649
29664
|
init_wiki_summary_names();
|
|
@@ -30958,8 +30973,376 @@ var init_src3 = __esm(() => {
|
|
|
30958
30973
|
});
|
|
30959
30974
|
|
|
30960
30975
|
// ../../packages/core/src/runtime-gateway.ts
|
|
30961
|
-
|
|
30962
|
-
|
|
30976
|
+
function record(value) {
|
|
30977
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
30978
|
+
}
|
|
30979
|
+
async function json(response) {
|
|
30980
|
+
try {
|
|
30981
|
+
return await response.json();
|
|
30982
|
+
} catch {
|
|
30983
|
+
throw new RuntimeGatewayError("Runtime Gateway returned invalid JSON", "protocol", response.status);
|
|
30984
|
+
}
|
|
30985
|
+
}
|
|
30986
|
+
function validHealth(value) {
|
|
30987
|
+
const body = record(value);
|
|
30988
|
+
return Boolean(body?.ok === true && body.v === RUNTIME_GATEWAY_VERSION && Array.isArray(body.capabilities) && typeof body.cursor === "number");
|
|
30989
|
+
}
|
|
30990
|
+
function validAcknowledgedMessage(value) {
|
|
30991
|
+
const message = record(value);
|
|
30992
|
+
return Boolean(message && typeof message.id === "string" && typeof message.topicId === "string" && typeof message.authorId === "string" && typeof message.text === "string" && typeof message.createdAt === "string");
|
|
30993
|
+
}
|
|
30994
|
+
function validTurnAck(value) {
|
|
30995
|
+
const body = record(value);
|
|
30996
|
+
return Boolean(body?.ok === true && body.v === RUNTIME_GATEWAY_VERSION && body.accepted === true && typeof body.deduplicated === "boolean" && typeof body.requestId === "string" && typeof body.clientMessageId === "string" && typeof body.topicId === "string" && typeof body.messageId === "string" && (body.message === undefined || validAcknowledgedMessage(body.message)) && typeof body.cursor === "number");
|
|
30997
|
+
}
|
|
30998
|
+
function validManagerTopic(value) {
|
|
30999
|
+
const topic = record(value);
|
|
31000
|
+
return Boolean(typeof topic?.id === "string" && typeof topic.title === "string" && topic.kind === "manager" && Array.isArray(topic.participants));
|
|
31001
|
+
}
|
|
31002
|
+
function validDeletedMessage(value) {
|
|
31003
|
+
const message = record(value);
|
|
31004
|
+
return Boolean(typeof message?.id === "string" && typeof message.topicId === "string" && typeof message.authorId === "string" && typeof message.text === "string" && message.deleted === true && typeof message.createdAt === "string");
|
|
31005
|
+
}
|
|
31006
|
+
function validCronJob(value) {
|
|
31007
|
+
const job = record(value);
|
|
31008
|
+
return Boolean(typeof job?.id === "string" && typeof job.name === "string" && typeof job.executionPrincipalUserId === "string" && typeof job.actorOwnerUserId === "string" && typeof job.topicId === "string" && typeof job.schedule === "string" && typeof job.enabled === "boolean" && typeof job.nextRunAt === "string" && typeof job.canMutate === "boolean");
|
|
31009
|
+
}
|
|
31010
|
+
async function* parseRuntimeGatewaySse(body) {
|
|
31011
|
+
const reader = body.getReader();
|
|
31012
|
+
const decoder = new TextDecoder;
|
|
31013
|
+
let buffer = "";
|
|
31014
|
+
let event = "message";
|
|
31015
|
+
let id;
|
|
31016
|
+
let data = [];
|
|
31017
|
+
const flush = () => {
|
|
31018
|
+
if (data.length === 0)
|
|
31019
|
+
return null;
|
|
31020
|
+
const raw = data.join(`
|
|
31021
|
+
`);
|
|
31022
|
+
data = [];
|
|
31023
|
+
try {
|
|
31024
|
+
return { event, id, data: JSON.parse(raw) };
|
|
31025
|
+
} catch {
|
|
31026
|
+
throw new RuntimeGatewayError("Runtime Gateway SSE event has invalid JSON", "protocol");
|
|
31027
|
+
} finally {
|
|
31028
|
+
event = "message";
|
|
31029
|
+
id = undefined;
|
|
31030
|
+
}
|
|
31031
|
+
};
|
|
31032
|
+
try {
|
|
31033
|
+
while (true) {
|
|
31034
|
+
const chunk = await reader.read();
|
|
31035
|
+
if (chunk.done)
|
|
31036
|
+
break;
|
|
31037
|
+
buffer += decoder.decode(chunk.value, { stream: true }).replace(/\r\n/g, `
|
|
31038
|
+
`);
|
|
31039
|
+
let newline = buffer.indexOf(`
|
|
31040
|
+
`);
|
|
31041
|
+
while (newline >= 0) {
|
|
31042
|
+
const line = buffer.slice(0, newline);
|
|
31043
|
+
buffer = buffer.slice(newline + 1);
|
|
31044
|
+
if (line === "") {
|
|
31045
|
+
const parsed2 = flush();
|
|
31046
|
+
if (parsed2)
|
|
31047
|
+
yield parsed2;
|
|
31048
|
+
} else if (line.startsWith("event:")) {
|
|
31049
|
+
event = line.slice(6).trim() || "message";
|
|
31050
|
+
} else if (line.startsWith("id:")) {
|
|
31051
|
+
const parsed2 = Number.parseInt(line.slice(3).trim(), 10);
|
|
31052
|
+
if (Number.isFinite(parsed2))
|
|
31053
|
+
id = parsed2;
|
|
31054
|
+
} else if (line.startsWith("data:")) {
|
|
31055
|
+
data.push(line.slice(5).trimStart());
|
|
31056
|
+
}
|
|
31057
|
+
newline = buffer.indexOf(`
|
|
31058
|
+
`);
|
|
31059
|
+
}
|
|
31060
|
+
}
|
|
31061
|
+
buffer += decoder.decode();
|
|
31062
|
+
if (buffer.startsWith("data:"))
|
|
31063
|
+
data.push(buffer.slice(5).trimStart());
|
|
31064
|
+
const parsed = flush();
|
|
31065
|
+
if (parsed)
|
|
31066
|
+
yield parsed;
|
|
31067
|
+
} finally {
|
|
31068
|
+
reader.releaseLock();
|
|
31069
|
+
}
|
|
31070
|
+
}
|
|
31071
|
+
|
|
31072
|
+
class RuntimeGatewayClient {
|
|
31073
|
+
options;
|
|
31074
|
+
pathPrefix;
|
|
31075
|
+
request;
|
|
31076
|
+
requestTimeoutMs;
|
|
31077
|
+
compactTimeoutMs;
|
|
31078
|
+
constructor(options) {
|
|
31079
|
+
this.options = options;
|
|
31080
|
+
this.pathPrefix = options.pathPrefix ?? RUNTIME_GATEWAY_CONTROL_PATH;
|
|
31081
|
+
this.request = options.fetch ?? fetch;
|
|
31082
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 5000;
|
|
31083
|
+
this.compactTimeoutMs = options.compactTimeoutMs ?? 5 * 60000;
|
|
31084
|
+
}
|
|
31085
|
+
endpoint(path) {
|
|
31086
|
+
if (!this.options.baseUrl || !this.options.token) {
|
|
31087
|
+
throw new RuntimeGatewayError("Runtime Gateway is not configured", "config");
|
|
31088
|
+
}
|
|
31089
|
+
return `${this.options.baseUrl.replace(/\/+$/, "")}${this.pathPrefix}${path}`;
|
|
31090
|
+
}
|
|
31091
|
+
async authorization() {
|
|
31092
|
+
try {
|
|
31093
|
+
const token = typeof this.options.token === "function" ? await this.options.token() : this.options.token;
|
|
31094
|
+
return `Bearer ${token}`;
|
|
31095
|
+
} catch (error2) {
|
|
31096
|
+
throw new RuntimeGatewayError(error2 instanceof Error ? error2.message : "Runtime Gateway token resolution failed", "config");
|
|
31097
|
+
}
|
|
31098
|
+
}
|
|
31099
|
+
async send(path, init, timeoutMs) {
|
|
31100
|
+
const authorization = await this.authorization();
|
|
31101
|
+
try {
|
|
31102
|
+
return await this.request(this.endpoint(path), {
|
|
31103
|
+
...init,
|
|
31104
|
+
headers: { authorization, ...init.headers },
|
|
31105
|
+
signal: timeoutMs === undefined ? init.signal : AbortSignal.timeout(timeoutMs)
|
|
31106
|
+
});
|
|
31107
|
+
} catch (error2) {
|
|
31108
|
+
if (error2 instanceof DOMException && error2.name === "TimeoutError") {
|
|
31109
|
+
throw new RuntimeGatewayError("Runtime Gateway request timed out", "timeout");
|
|
31110
|
+
}
|
|
31111
|
+
throw new RuntimeGatewayError("Runtime Gateway request failed", "transport");
|
|
31112
|
+
}
|
|
31113
|
+
}
|
|
31114
|
+
async health() {
|
|
31115
|
+
const response = await this.send("/health", { method: "GET" }, this.requestTimeoutMs);
|
|
31116
|
+
if (!response.ok) {
|
|
31117
|
+
throw new RuntimeGatewayError("Runtime Gateway health request failed", "http", response.status);
|
|
31118
|
+
}
|
|
31119
|
+
const body = await json(response);
|
|
31120
|
+
if (!validHealth(body)) {
|
|
31121
|
+
throw new RuntimeGatewayError("Runtime Gateway health response is incompatible", "protocol");
|
|
31122
|
+
}
|
|
31123
|
+
return body;
|
|
31124
|
+
}
|
|
31125
|
+
async submitTurn(input) {
|
|
31126
|
+
const response = await this.send("/turns", {
|
|
31127
|
+
method: "POST",
|
|
31128
|
+
headers: { "content-type": "application/json" },
|
|
31129
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
31130
|
+
}, this.requestTimeoutMs);
|
|
31131
|
+
if (response.status !== 202) {
|
|
31132
|
+
throw new RuntimeGatewayError("Runtime Gateway turn was not accepted", "http", response.status);
|
|
31133
|
+
}
|
|
31134
|
+
const body = await json(response);
|
|
31135
|
+
if (!validTurnAck(body)) {
|
|
31136
|
+
throw new RuntimeGatewayError("Runtime Gateway turn acknowledgement is incompatible", "protocol");
|
|
31137
|
+
}
|
|
31138
|
+
return body;
|
|
31139
|
+
}
|
|
31140
|
+
async deriveTopic(topicId, input) {
|
|
31141
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/derive`, {
|
|
31142
|
+
method: "POST",
|
|
31143
|
+
headers: { "content-type": "application/json" },
|
|
31144
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
31145
|
+
}, this.requestTimeoutMs);
|
|
31146
|
+
if (response.status !== 201) {
|
|
31147
|
+
throw new RuntimeGatewayError("Runtime Gateway topic derive failed", "http", response.status);
|
|
31148
|
+
}
|
|
31149
|
+
const body = await json(response);
|
|
31150
|
+
const topic = record(body)?.topic;
|
|
31151
|
+
const topicRecord = record(topic);
|
|
31152
|
+
if (!topicRecord || typeof topicRecord.id !== "string" || typeof topicRecord.title !== "string") {
|
|
31153
|
+
throw new RuntimeGatewayError("Runtime Gateway topic derive response is incompatible", "protocol");
|
|
31154
|
+
}
|
|
31155
|
+
return topic;
|
|
31156
|
+
}
|
|
31157
|
+
async ensureManagerTopic(userId) {
|
|
31158
|
+
const response = await this.send("/manager-topic", {
|
|
31159
|
+
method: "POST",
|
|
31160
|
+
headers: { "content-type": "application/json" },
|
|
31161
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, userId })
|
|
31162
|
+
}, this.requestTimeoutMs);
|
|
31163
|
+
if (!response.ok) {
|
|
31164
|
+
throw new RuntimeGatewayError("Runtime Gateway manager topic request failed", "http", response.status);
|
|
31165
|
+
}
|
|
31166
|
+
const body = record(await json(response));
|
|
31167
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || !validManagerTopic(body.topic)) {
|
|
31168
|
+
throw new RuntimeGatewayError("Runtime Gateway manager topic response is incompatible", "protocol");
|
|
31169
|
+
}
|
|
31170
|
+
return body.topic;
|
|
31171
|
+
}
|
|
31172
|
+
async deleteMessage(topicId, messageId, actorUserId, allowAdmin = false) {
|
|
31173
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/messages/${encodeURIComponent(messageId)}`, {
|
|
31174
|
+
method: "DELETE",
|
|
31175
|
+
headers: { "content-type": "application/json" },
|
|
31176
|
+
body: JSON.stringify({
|
|
31177
|
+
v: RUNTIME_GATEWAY_VERSION,
|
|
31178
|
+
actorUserId,
|
|
31179
|
+
...allowAdmin ? { allowAdmin: true } : {}
|
|
31180
|
+
})
|
|
31181
|
+
}, this.requestTimeoutMs);
|
|
31182
|
+
if (!response.ok) {
|
|
31183
|
+
throw new RuntimeGatewayError("Runtime Gateway message delete failed", "http", response.status);
|
|
31184
|
+
}
|
|
31185
|
+
const body = record(await json(response));
|
|
31186
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || !validDeletedMessage(body.message)) {
|
|
31187
|
+
throw new RuntimeGatewayError("Runtime Gateway message delete response is incompatible", "protocol");
|
|
31188
|
+
}
|
|
31189
|
+
return body.message;
|
|
31190
|
+
}
|
|
31191
|
+
async cronRequest(path, init) {
|
|
31192
|
+
const response = await this.send(path, init, this.requestTimeoutMs);
|
|
31193
|
+
if (!response.ok) {
|
|
31194
|
+
throw new RuntimeGatewayError("Runtime Gateway cron request failed", "http", response.status);
|
|
31195
|
+
}
|
|
31196
|
+
const body = record(await json(response));
|
|
31197
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION) {
|
|
31198
|
+
throw new RuntimeGatewayError("Runtime Gateway cron response is incompatible", "protocol");
|
|
31199
|
+
}
|
|
31200
|
+
return body;
|
|
31201
|
+
}
|
|
31202
|
+
async listCronScripts() {
|
|
31203
|
+
const body = await this.cronRequest("/cron/scripts", { method: "GET" });
|
|
31204
|
+
if (!Array.isArray(body.scripts) || !body.scripts.every((value) => typeof value === "string")) {
|
|
31205
|
+
throw new RuntimeGatewayError("Runtime Gateway cron scripts are incompatible", "protocol");
|
|
31206
|
+
}
|
|
31207
|
+
return body.scripts;
|
|
31208
|
+
}
|
|
31209
|
+
async listCronJobs(userId, actorUserId, actorIsAdmin = false) {
|
|
31210
|
+
const search = new URLSearchParams({
|
|
31211
|
+
user: userId,
|
|
31212
|
+
actorUserId,
|
|
31213
|
+
actorIsAdmin: actorIsAdmin ? "1" : "0"
|
|
31214
|
+
});
|
|
31215
|
+
const body = await this.cronRequest(`/cron/jobs?${search}`, { method: "GET" });
|
|
31216
|
+
if (!Array.isArray(body.jobs) || !body.jobs.every(validCronJob)) {
|
|
31217
|
+
throw new RuntimeGatewayError("Runtime Gateway cron jobs are incompatible", "protocol");
|
|
31218
|
+
}
|
|
31219
|
+
return body.jobs;
|
|
31220
|
+
}
|
|
31221
|
+
async createCronJob(input) {
|
|
31222
|
+
const body = await this.cronRequest("/cron/jobs", {
|
|
31223
|
+
method: "POST",
|
|
31224
|
+
headers: { "content-type": "application/json" },
|
|
31225
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
31226
|
+
});
|
|
31227
|
+
if (!validCronJob(body.job)) {
|
|
31228
|
+
throw new RuntimeGatewayError("Runtime Gateway cron job is incompatible", "protocol");
|
|
31229
|
+
}
|
|
31230
|
+
return body.job;
|
|
31231
|
+
}
|
|
31232
|
+
async updateCronJob(jobId, userId, actorUserId, patch, actorIsAdmin = false) {
|
|
31233
|
+
const body = await this.cronRequest(`/cron/jobs/${encodeURIComponent(jobId)}`, {
|
|
31234
|
+
method: "PATCH",
|
|
31235
|
+
headers: { "content-type": "application/json" },
|
|
31236
|
+
body: JSON.stringify({
|
|
31237
|
+
v: RUNTIME_GATEWAY_VERSION,
|
|
31238
|
+
userId,
|
|
31239
|
+
actorUserId,
|
|
31240
|
+
actorIsAdmin,
|
|
31241
|
+
patch
|
|
31242
|
+
})
|
|
31243
|
+
});
|
|
31244
|
+
if (!validCronJob(body.job)) {
|
|
31245
|
+
throw new RuntimeGatewayError("Runtime Gateway cron job is incompatible", "protocol");
|
|
31246
|
+
}
|
|
31247
|
+
return body.job;
|
|
31248
|
+
}
|
|
31249
|
+
async deleteCronJob(jobId, userId, actorUserId, actorIsAdmin = false) {
|
|
31250
|
+
const search = new URLSearchParams({
|
|
31251
|
+
user: userId,
|
|
31252
|
+
actorUserId,
|
|
31253
|
+
actorIsAdmin: actorIsAdmin ? "1" : "0"
|
|
31254
|
+
});
|
|
31255
|
+
const body = await this.cronRequest(`/cron/jobs/${encodeURIComponent(jobId)}?${search}`, {
|
|
31256
|
+
method: "DELETE"
|
|
31257
|
+
});
|
|
31258
|
+
if (typeof body.deleted !== "string") {
|
|
31259
|
+
throw new RuntimeGatewayError("Runtime Gateway cron delete is incompatible", "protocol");
|
|
31260
|
+
}
|
|
31261
|
+
return body.deleted;
|
|
31262
|
+
}
|
|
31263
|
+
async* events(after, topicId, signal) {
|
|
31264
|
+
const search = new URLSearchParams({ after: String(Math.max(0, after)) });
|
|
31265
|
+
if (topicId)
|
|
31266
|
+
search.set("topicId", topicId);
|
|
31267
|
+
const response = await this.send(`/events?${search}`, { method: "GET", signal });
|
|
31268
|
+
if (!response.ok || !response.body) {
|
|
31269
|
+
throw new RuntimeGatewayError("Runtime Gateway event stream failed", "http", response.status);
|
|
31270
|
+
}
|
|
31271
|
+
yield* parseRuntimeGatewaySse(response.body);
|
|
31272
|
+
}
|
|
31273
|
+
async getTopicUsage(topicId, userId) {
|
|
31274
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/usage?user=${encodeURIComponent(userId)}`, { method: "GET" }, this.requestTimeoutMs);
|
|
31275
|
+
if (!response.ok) {
|
|
31276
|
+
throw new RuntimeGatewayError("Runtime Gateway topic usage request failed", "http", response.status);
|
|
31277
|
+
}
|
|
31278
|
+
const usage = record(record(await json(response))?.usage);
|
|
31279
|
+
const read = (key) => {
|
|
31280
|
+
const value = usage?.[key];
|
|
31281
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
31282
|
+
};
|
|
31283
|
+
return {
|
|
31284
|
+
queries: read("queries"),
|
|
31285
|
+
inputTokens: read("inputTokens"),
|
|
31286
|
+
outputTokens: read("outputTokens"),
|
|
31287
|
+
cacheCreationInputTokens: read("cacheCreationInputTokens"),
|
|
31288
|
+
cacheReadInputTokens: read("cacheReadInputTokens"),
|
|
31289
|
+
estimatedCostUsd: read("estimatedCostUsd")
|
|
31290
|
+
};
|
|
31291
|
+
}
|
|
31292
|
+
async abortTurn(topicId, userId) {
|
|
31293
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/abort`, {
|
|
31294
|
+
method: "POST",
|
|
31295
|
+
headers: { "content-type": "application/json" },
|
|
31296
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, userId })
|
|
31297
|
+
}, this.requestTimeoutMs);
|
|
31298
|
+
if (!response.ok) {
|
|
31299
|
+
throw new RuntimeGatewayError("Runtime Gateway turn abort failed", "http", response.status);
|
|
31300
|
+
}
|
|
31301
|
+
const body = record(await json(response));
|
|
31302
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || typeof body.aborted !== "boolean") {
|
|
31303
|
+
throw new RuntimeGatewayError("Runtime Gateway turn abort response is incompatible", "protocol");
|
|
31304
|
+
}
|
|
31305
|
+
return body.aborted;
|
|
31306
|
+
}
|
|
31307
|
+
async resetSession(topicId, userId, reason, actorUserId) {
|
|
31308
|
+
return this.sessionCommand(topicId, userId, "reset", reason, actorUserId);
|
|
31309
|
+
}
|
|
31310
|
+
async compactSession(topicId, userId, reason, actorUserId) {
|
|
31311
|
+
return this.sessionCommand(topicId, userId, "compact", reason, actorUserId);
|
|
31312
|
+
}
|
|
31313
|
+
async sessionCommand(topicId, userId, command, reason, actorUserId) {
|
|
31314
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/session/${command}`, {
|
|
31315
|
+
method: "POST",
|
|
31316
|
+
headers: { "content-type": "application/json" },
|
|
31317
|
+
body: JSON.stringify({
|
|
31318
|
+
v: RUNTIME_GATEWAY_VERSION,
|
|
31319
|
+
userId,
|
|
31320
|
+
...actorUserId ? { actorUserId } : {},
|
|
31321
|
+
...reason ? { reason } : {}
|
|
31322
|
+
})
|
|
31323
|
+
}, command === "compact" ? this.compactTimeoutMs : this.requestTimeoutMs);
|
|
31324
|
+
if (!response.ok) {
|
|
31325
|
+
throw new RuntimeGatewayError(`Runtime Gateway session ${command} failed`, "http", response.status);
|
|
31326
|
+
}
|
|
31327
|
+
const body = record(await json(response));
|
|
31328
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION) {
|
|
31329
|
+
throw new RuntimeGatewayError(`Runtime Gateway session ${command} response is incompatible`, "protocol");
|
|
31330
|
+
}
|
|
31331
|
+
return typeof body.result === "string" ? body.result : undefined;
|
|
31332
|
+
}
|
|
31333
|
+
}
|
|
31334
|
+
var RUNTIME_GATEWAY_VERSION = 1, RUNTIME_GATEWAY_CONTROL_PATH = "/api/v1/control/runtime/v1", RuntimeGatewayError;
|
|
31335
|
+
var init_runtime_gateway = __esm(() => {
|
|
31336
|
+
RuntimeGatewayError = class RuntimeGatewayError extends Error {
|
|
31337
|
+
kind;
|
|
31338
|
+
status;
|
|
31339
|
+
constructor(message, kind, status) {
|
|
31340
|
+
super(message);
|
|
31341
|
+
this.kind = kind;
|
|
31342
|
+
this.status = status;
|
|
31343
|
+
}
|
|
31344
|
+
};
|
|
31345
|
+
});
|
|
30963
31346
|
|
|
30964
31347
|
// ../../packages/core/src/cron-host.ts
|
|
30965
31348
|
var init_cron_host = __esm(async () => {
|
|
@@ -33542,21 +33925,22 @@ function createNodeControlHandler(options) {
|
|
|
33542
33925
|
const body = await bodyRecord(req);
|
|
33543
33926
|
if (body.v !== NODE_RUNTIME_CONTRACT_VERSION)
|
|
33544
33927
|
return jsonError(400, "Unsupported v");
|
|
33545
|
-
const topicId = requiredText(body.topicId, "topicId");
|
|
33928
|
+
const topicId = asTopicId(requiredText(body.topicId, "topicId"));
|
|
33546
33929
|
const turnTopic = getTopic(topicId);
|
|
33547
33930
|
if (turnTopic && !topicInRequestScope(req, turnTopic)) {
|
|
33548
33931
|
return jsonError(404, "Topic not found");
|
|
33549
33932
|
}
|
|
33550
|
-
const userId = requiredText(body.userId, "userId");
|
|
33551
|
-
const actorUserId = body.actorUserId === undefined ? undefined : requiredText(body.actorUserId, "actorUserId");
|
|
33933
|
+
const userId = asUserId(requiredText(body.userId, "userId"));
|
|
33934
|
+
const actorUserId = body.actorUserId === undefined ? undefined : asUserId(requiredText(body.actorUserId, "actorUserId"));
|
|
33552
33935
|
const actorLabel = body.actorLabel === undefined ? undefined : requiredText(body.actorLabel, "actorLabel");
|
|
33553
|
-
const vaultUserId = body.vaultUserId === undefined ? undefined : requiredText(body.vaultUserId, "vaultUserId");
|
|
33936
|
+
const vaultUserId = body.vaultUserId === undefined ? undefined : asUserId(requiredText(body.vaultUserId, "vaultUserId"));
|
|
33937
|
+
const sourceAdapter = body.sourceAdapter === undefined ? undefined : requiredText(body.sourceAdapter, "sourceAdapter");
|
|
33554
33938
|
if (typeof body.text !== "string")
|
|
33555
33939
|
throw new ControlRequestError("text is required");
|
|
33556
33940
|
const text2 = body.text;
|
|
33557
|
-
const clientMessageId = requiredText(body.clientMessageId, "clientMessageId");
|
|
33558
|
-
const requestId = body.requestId === undefined ? undefined : requiredText(body.requestId, "requestId");
|
|
33559
|
-
const threadRootId = body.threadRootId === undefined ? undefined : requiredText(body.threadRootId, "threadRootId");
|
|
33941
|
+
const clientMessageId = asClientMessageId(requiredText(body.clientMessageId, "clientMessageId"));
|
|
33942
|
+
const requestId = body.requestId === undefined ? undefined : asRequestId(requiredText(body.requestId, "requestId"));
|
|
33943
|
+
const threadRootId = body.threadRootId === undefined ? undefined : asMessageId(requiredText(body.threadRootId, "threadRootId"));
|
|
33560
33944
|
const attachments2 = body.attachments === undefined ? [] : Array.isArray(body.attachments) && body.attachments.every((value) => typeof value === "string" && value.trim()) ? body.attachments : (() => {
|
|
33561
33945
|
throw new ControlRequestError("attachments must be an array of file ids");
|
|
33562
33946
|
})();
|
|
@@ -33584,6 +33968,7 @@ function createNodeControlHandler(options) {
|
|
|
33584
33968
|
actorUserId,
|
|
33585
33969
|
actorLabel,
|
|
33586
33970
|
vaultUserId,
|
|
33971
|
+
sourceAdapter,
|
|
33587
33972
|
text: text2,
|
|
33588
33973
|
clientMessageId,
|
|
33589
33974
|
requestId,
|
|
@@ -33604,6 +33989,7 @@ function createNodeControlHandler(options) {
|
|
|
33604
33989
|
clientMessageId: submission.clientMessageId,
|
|
33605
33990
|
topicId: submission.topicId,
|
|
33606
33991
|
messageId: submission.messageId,
|
|
33992
|
+
message: submission.message,
|
|
33607
33993
|
cursor: submission.ackCursor
|
|
33608
33994
|
}, { status: 202 });
|
|
33609
33995
|
}
|
|
@@ -36468,7 +36854,7 @@ function parseVisualizationReference(value) {
|
|
|
36468
36854
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
36469
36855
|
return null;
|
|
36470
36856
|
const keys = Object.keys(parsed);
|
|
36471
|
-
if (keys.length < 1 || keys.length >
|
|
36857
|
+
if (keys.length < 1 || keys.length > 3 || keys.some((key) => key !== "path" && key !== "mode" && key !== "title")) {
|
|
36472
36858
|
return null;
|
|
36473
36859
|
}
|
|
36474
36860
|
const candidate = parsed;
|
|
@@ -36477,13 +36863,17 @@ function parseVisualizationReference(value) {
|
|
|
36477
36863
|
}
|
|
36478
36864
|
if (candidate.mode !== undefined && candidate.mode !== "wide")
|
|
36479
36865
|
return null;
|
|
36866
|
+
if (candidate.title !== undefined && (typeof candidate.title !== "string" || candidate.title.length === 0 || candidate.title.length > MAX_VISUALIZATION_TITLE_LENGTH || hasControlCharacter(candidate.title))) {
|
|
36867
|
+
return null;
|
|
36868
|
+
}
|
|
36480
36869
|
return {
|
|
36481
36870
|
path: candidate.path,
|
|
36482
36871
|
name: basename12(candidate.path) || candidate.path,
|
|
36483
|
-
...candidate.mode === "wide" ? { mode: "wide" } : {}
|
|
36872
|
+
...candidate.mode === "wide" ? { mode: "wide" } : {},
|
|
36873
|
+
...typeof candidate.title === "string" ? { title: candidate.title } : {}
|
|
36484
36874
|
};
|
|
36485
36875
|
}
|
|
36486
|
-
var VISUALIZE_PREFIX = "\uE200visualize\uE202", VISUALIZE_SUFFIX = "\uE201", MAX_VISUALIZATION_REFERENCE_LENGTH = 8192, MAX_VISUALIZATION_PATH_LENGTH = 4096;
|
|
36876
|
+
var VISUALIZE_PREFIX = "\uE200visualize\uE202", VISUALIZE_SUFFIX = "\uE201", MAX_VISUALIZATION_REFERENCE_LENGTH = 8192, MAX_VISUALIZATION_PATH_LENGTH = 4096, MAX_VISUALIZATION_TITLE_LENGTH = 200;
|
|
36487
36877
|
var init_visualization = () => {};
|
|
36488
36878
|
|
|
36489
36879
|
// ../../adapters/terminal/src/render.ts
|
|
@@ -36934,7 +37324,8 @@ function renderMarkdown(value, width) {
|
|
|
36934
37324
|
if (visualization) {
|
|
36935
37325
|
flushTable();
|
|
36936
37326
|
const mode = visualization.mode ? ` \xB7 ${visualization.mode}` : "";
|
|
36937
|
-
|
|
37327
|
+
const label = visualization.title ?? visualization.name;
|
|
37328
|
+
result.push(line(` \u25C7 visualization${mode} \xB7 ${label}`, {
|
|
36938
37329
|
fg: theme.cyan,
|
|
36939
37330
|
bg: theme.surfaceRaised,
|
|
36940
37331
|
bold: true
|
|
@@ -38628,6 +39019,22 @@ __export(exports_client, {
|
|
|
38628
39019
|
INITIAL_MESSAGE_HISTORY_LIMIT: () => INITIAL_MESSAGE_HISTORY_LIMIT,
|
|
38629
39020
|
EmbeddedNegotiumClient: () => EmbeddedNegotiumClient
|
|
38630
39021
|
});
|
|
39022
|
+
import { randomUUID as randomUUID28 } from "crypto";
|
|
39023
|
+
function terminalClientMessageId() {
|
|
39024
|
+
return `terminal:${randomUUID28()}`;
|
|
39025
|
+
}
|
|
39026
|
+
function retryableGatewaySubmission(error2) {
|
|
39027
|
+
return error2 instanceof RuntimeGatewayError && (error2.kind === "transport" || error2.kind === "timeout" || error2.kind === "http" && (error2.status ?? 0) >= 500);
|
|
39028
|
+
}
|
|
39029
|
+
async function submitTerminalGatewayTurn(gateway, input) {
|
|
39030
|
+
try {
|
|
39031
|
+
return await gateway.submitTurn(input);
|
|
39032
|
+
} catch (error2) {
|
|
39033
|
+
if (!retryableGatewaySubmission(error2))
|
|
39034
|
+
throw error2;
|
|
39035
|
+
return gateway.submitTurn(input);
|
|
39036
|
+
}
|
|
39037
|
+
}
|
|
38631
39038
|
|
|
38632
39039
|
class EmbeddedNegotiumClient {
|
|
38633
39040
|
#userId;
|
|
@@ -38757,11 +39164,14 @@ class EmbeddedNegotiumClient {
|
|
|
38757
39164
|
await topicService.delete({ topicId: topic.id, userId: this.#userId });
|
|
38758
39165
|
}
|
|
38759
39166
|
sendMessage(topic, text2) {
|
|
38760
|
-
|
|
39167
|
+
const clientMessageId = terminalClientMessageId();
|
|
39168
|
+
return submitRuntimeGatewayTurn({
|
|
38761
39169
|
topic,
|
|
38762
39170
|
userId: this.#userId,
|
|
38763
39171
|
text: text2,
|
|
38764
39172
|
sourceAdapter: "terminal",
|
|
39173
|
+
clientMessageId,
|
|
39174
|
+
requestId: clientMessageId,
|
|
38765
39175
|
visualTools: false,
|
|
38766
39176
|
fileDeliveryTools: false
|
|
38767
39177
|
}).message;
|
|
@@ -38822,6 +39232,7 @@ class RemoteNegotiumClient {
|
|
|
38822
39232
|
#userId;
|
|
38823
39233
|
#baseUrl;
|
|
38824
39234
|
#token;
|
|
39235
|
+
#gateway;
|
|
38825
39236
|
#onEvent = null;
|
|
38826
39237
|
#eventAbort = null;
|
|
38827
39238
|
#eventTask = null;
|
|
@@ -38833,6 +39244,7 @@ class RemoteNegotiumClient {
|
|
|
38833
39244
|
throw new Error("Remote node control requires HTTPS or loopback HTTP");
|
|
38834
39245
|
}
|
|
38835
39246
|
this.#token = options.token;
|
|
39247
|
+
this.#gateway = new RuntimeGatewayClient({ baseUrl: this.#baseUrl, token: this.#token });
|
|
38836
39248
|
}
|
|
38837
39249
|
async start(onEvent) {
|
|
38838
39250
|
if (this.#started)
|
|
@@ -38949,11 +39361,26 @@ class RemoteNegotiumClient {
|
|
|
38949
39361
|
await this.#request(`/topics/${encodeURIComponent(topic.id)}?user=${encodeURIComponent(this.#userId)}`, { method: "DELETE" });
|
|
38950
39362
|
}
|
|
38951
39363
|
async sendMessage(topic, text2) {
|
|
38952
|
-
const
|
|
38953
|
-
|
|
38954
|
-
|
|
39364
|
+
const clientMessageId = terminalClientMessageId();
|
|
39365
|
+
const acknowledgement = await submitTerminalGatewayTurn(this.#gateway, {
|
|
39366
|
+
topicId: topic.id,
|
|
39367
|
+
userId: this.#userId,
|
|
39368
|
+
sourceAdapter: "terminal",
|
|
39369
|
+
text: text2,
|
|
39370
|
+
clientMessageId,
|
|
39371
|
+
requestId: clientMessageId,
|
|
39372
|
+
visualTools: false,
|
|
39373
|
+
fileDeliveryTools: false
|
|
38955
39374
|
});
|
|
38956
|
-
return
|
|
39375
|
+
return acknowledgement.message ?? {
|
|
39376
|
+
id: acknowledgement.messageId,
|
|
39377
|
+
topicId: topic.id,
|
|
39378
|
+
authorId: this.#userId,
|
|
39379
|
+
sourceAdapter: "terminal",
|
|
39380
|
+
sourceMessageId: clientMessageId,
|
|
39381
|
+
text: text2,
|
|
39382
|
+
createdAt: new Date().toISOString()
|
|
39383
|
+
};
|
|
38957
39384
|
}
|
|
38958
39385
|
async answerQuestion(topicId, messageId, label) {
|
|
38959
39386
|
try {
|
|
@@ -39133,6 +39560,7 @@ class RemoteNegotiumClient {
|
|
|
39133
39560
|
var MESSAGE_HISTORY_PAGE_SIZE = 50, INITIAL_MESSAGE_HISTORY_PAGE_COUNT = 3, INITIAL_MESSAGE_HISTORY_LIMIT;
|
|
39134
39561
|
var init_client = __esm(async () => {
|
|
39135
39562
|
await init_src();
|
|
39563
|
+
init_runtime_gateway();
|
|
39136
39564
|
await init_storage_public();
|
|
39137
39565
|
await init_src5();
|
|
39138
39566
|
await init_history_store();
|
|
@@ -42719,12 +43147,15 @@ var init_telegram_api = __esm(() => {
|
|
|
42719
43147
|
});
|
|
42720
43148
|
|
|
42721
43149
|
// ../../adapters/telegram/src/commands.ts
|
|
42722
|
-
function resolveTelegramTopicArg(argument, userId) {
|
|
42723
|
-
const byName = getTopicByNameForUser(argument, userId, {
|
|
43150
|
+
function resolveTelegramTopicArg(argument, userId, surfaceScope) {
|
|
43151
|
+
const byName = getTopicByNameForUser(argument, userId, {
|
|
43152
|
+
surface: "telegram",
|
|
43153
|
+
surfaceScope
|
|
43154
|
+
});
|
|
42724
43155
|
if (byName)
|
|
42725
43156
|
return byName;
|
|
42726
43157
|
const byId = getTopic(argument);
|
|
42727
|
-
return byId?.surface === "telegram" ? byId : null;
|
|
43158
|
+
return byId?.surface === "telegram" && (byId.surfaceScope ?? null) === surfaceScope ? byId : null;
|
|
42728
43159
|
}
|
|
42729
43160
|
function createTelegramCommandRouter(context) {
|
|
42730
43161
|
const { userId, reply, currentTopicId, bindMapping, loadTopic, unloadTopic, abortTurn } = context;
|
|
@@ -42762,7 +43193,7 @@ function createTelegramCommandRouter(context) {
|
|
|
42762
43193
|
return;
|
|
42763
43194
|
}
|
|
42764
43195
|
try {
|
|
42765
|
-
const topic = context.getOrCreateTopic(`${context.titleFor(chatId, threadId)}-${argument}`, argument);
|
|
43196
|
+
const topic = context.getOrCreateTopic(chatId, `${context.titleFor(chatId, threadId)}-${argument}`, argument);
|
|
42766
43197
|
bindMapping(chatId, threadId, topic.id);
|
|
42767
43198
|
reply(chatId, threadId, `agent set to ${argument} \u2014 topic "${topic.title}"`);
|
|
42768
43199
|
} catch (err2) {
|
|
@@ -42771,7 +43202,10 @@ function createTelegramCommandRouter(context) {
|
|
|
42771
43202
|
return;
|
|
42772
43203
|
}
|
|
42773
43204
|
case "/topics": {
|
|
42774
|
-
const topics = listTopics({
|
|
43205
|
+
const topics = listTopics({
|
|
43206
|
+
surface: "telegram",
|
|
43207
|
+
surfaceScope: context.surfaceScopeFor(chatId)
|
|
43208
|
+
}).filter((topic) => isTopicVisible(topic) && topic.participants.some((person) => person.userId === userId));
|
|
42775
43209
|
reply(chatId, threadId, topics.length ? topics.map((topic) => `- ${topic.title}${topic.agent ? ` (${topic.agent})` : ""}`).join(`
|
|
42776
43210
|
`) : "no topics");
|
|
42777
43211
|
return;
|
|
@@ -42802,9 +43236,10 @@ function createTelegramCommandRouter(context) {
|
|
|
42802
43236
|
userId,
|
|
42803
43237
|
kind: "agent",
|
|
42804
43238
|
surface: "telegram",
|
|
43239
|
+
surfaceScope: context.surfaceScopeFor(chatId),
|
|
42805
43240
|
...context.defaultAgent ? { agent: context.defaultAgent } : {}
|
|
42806
43241
|
};
|
|
42807
|
-
const fromForumGeneral = context.
|
|
43242
|
+
const fromForumGeneral = context.isForumGeneral(chatId, threadId);
|
|
42808
43243
|
const topic = fromForumGeneral ? topicService.create(createOptions) : context.registerTopic(createOptions);
|
|
42809
43244
|
if (fromForumGeneral) {
|
|
42810
43245
|
reply(chatId, threadId, `creating new topic "${topic.title}"`);
|
|
@@ -42822,7 +43257,7 @@ function createTelegramCommandRouter(context) {
|
|
|
42822
43257
|
reply(chatId, threadId, "usage: /load <topic>");
|
|
42823
43258
|
return;
|
|
42824
43259
|
}
|
|
42825
|
-
const candidate = resolveTelegramTopicArg(argument, userId);
|
|
43260
|
+
const candidate = resolveTelegramTopicArg(argument, userId, context.surfaceScopeFor(chatId));
|
|
42826
43261
|
const topic = candidate && isTopicVisible(candidate) ? candidate : null;
|
|
42827
43262
|
if (!topic || !loadTopic(chatId, topic.id, threadId)) {
|
|
42828
43263
|
reply(chatId, threadId, `no visible topic matching "${argument}"`);
|
|
@@ -42867,7 +43302,7 @@ function createTelegramCommandRouter(context) {
|
|
|
42867
43302
|
case "/del!": {
|
|
42868
43303
|
const force = command === "/del!";
|
|
42869
43304
|
const topicId = currentTopicId(chatId, threadId);
|
|
42870
|
-
const topic = argument ? resolveTelegramTopicArg(argument, userId) : topicId ? getTopic(topicId) : null;
|
|
43305
|
+
const topic = argument ? resolveTelegramTopicArg(argument, userId, context.surfaceScopeFor(chatId)) : topicId ? getTopic(topicId) : null;
|
|
42871
43306
|
if (!topic) {
|
|
42872
43307
|
reply(chatId, threadId, argument ? `no topic named "${argument}"` : "this chat has no topic \u2014 usage: /del [name]");
|
|
42873
43308
|
return;
|
|
@@ -42952,6 +43387,12 @@ function createTables(db4) {
|
|
|
42952
43387
|
key TEXT PRIMARY KEY,
|
|
42953
43388
|
value TEXT NOT NULL
|
|
42954
43389
|
)`);
|
|
43390
|
+
db4.run(`CREATE TABLE IF NOT EXISTS telegram_groups (
|
|
43391
|
+
chat_id INTEGER PRIMARY KEY,
|
|
43392
|
+
title TEXT,
|
|
43393
|
+
owner_telegram_user_id INTEGER,
|
|
43394
|
+
created_at TEXT NOT NULL
|
|
43395
|
+
)`);
|
|
42955
43396
|
db4.run(`CREATE TABLE IF NOT EXISTS runtime_inbox (
|
|
42956
43397
|
seq INTEGER PRIMARY KEY,
|
|
42957
43398
|
runtime_message_id TEXT,
|
|
@@ -42981,6 +43422,14 @@ function openMappingStore(path) {
|
|
|
42981
43422
|
const db4 = new Database2(dbPath);
|
|
42982
43423
|
migrateLegacySchema(db4);
|
|
42983
43424
|
createTables(db4);
|
|
43425
|
+
const legacyForum = db4.query("SELECT value FROM settings WHERE key = 'forum_chat_id'").get();
|
|
43426
|
+
if (legacyForum) {
|
|
43427
|
+
const chatId = Number.parseInt(legacyForum.value, 10);
|
|
43428
|
+
if (Number.isSafeInteger(chatId)) {
|
|
43429
|
+
db4.run(`INSERT OR IGNORE INTO telegram_groups (chat_id, created_at)
|
|
43430
|
+
VALUES (?, ?)`, [chatId, new Date().toISOString()]);
|
|
43431
|
+
}
|
|
43432
|
+
}
|
|
42984
43433
|
migrateOutboxSchema(db4);
|
|
42985
43434
|
migrateRuntimeInboxSchema(db4);
|
|
42986
43435
|
return {
|
|
@@ -43033,6 +43482,32 @@ function openMappingStore(path) {
|
|
|
43033
43482
|
clearForumChatId() {
|
|
43034
43483
|
db4.run("DELETE FROM settings WHERE key = 'forum_chat_id'");
|
|
43035
43484
|
},
|
|
43485
|
+
loadGroups() {
|
|
43486
|
+
const rows = db4.query("SELECT chat_id, title, owner_telegram_user_id FROM telegram_groups ORDER BY created_at").all();
|
|
43487
|
+
return rows.map((row) => ({
|
|
43488
|
+
chatId: row.chat_id,
|
|
43489
|
+
...row.title ? { title: row.title } : {},
|
|
43490
|
+
...row.owner_telegram_user_id !== null ? { ownerTelegramUserId: row.owner_telegram_user_id } : {}
|
|
43491
|
+
}));
|
|
43492
|
+
},
|
|
43493
|
+
saveGroup(group) {
|
|
43494
|
+
db4.run(`INSERT INTO telegram_groups (chat_id, title, owner_telegram_user_id, created_at)
|
|
43495
|
+
VALUES (?, ?, ?, ?)
|
|
43496
|
+
ON CONFLICT(chat_id) DO UPDATE SET
|
|
43497
|
+
title = COALESCE(excluded.title, telegram_groups.title),
|
|
43498
|
+
owner_telegram_user_id = COALESCE(
|
|
43499
|
+
excluded.owner_telegram_user_id,
|
|
43500
|
+
telegram_groups.owner_telegram_user_id
|
|
43501
|
+
)`, [
|
|
43502
|
+
group.chatId,
|
|
43503
|
+
group.title ?? null,
|
|
43504
|
+
group.ownerTelegramUserId ?? null,
|
|
43505
|
+
new Date().toISOString()
|
|
43506
|
+
]);
|
|
43507
|
+
},
|
|
43508
|
+
deleteGroup(chatId) {
|
|
43509
|
+
db4.run("DELETE FROM telegram_groups WHERE chat_id = ?", [chatId]);
|
|
43510
|
+
},
|
|
43036
43511
|
isFlagSet(key) {
|
|
43037
43512
|
return db4.query("SELECT 1 FROM settings WHERE key = ?").get(key) !== null;
|
|
43038
43513
|
},
|
|
@@ -43194,7 +43669,7 @@ function createTelegramMediaIntake(options) {
|
|
|
43194
43669
|
if (typeof client.getFileLink !== "function") {
|
|
43195
43670
|
logger.warn({ chatId }, "telegram adapter: media message but client lacks getFileLink \u2014 caption only");
|
|
43196
43671
|
if (caption)
|
|
43197
|
-
runTurn(topic, caption, chatId, threadId);
|
|
43672
|
+
runTurn(topic, caption, chatId, threadId, msg);
|
|
43198
43673
|
else
|
|
43199
43674
|
reply(chatId, threadId, "this bot cannot download attachments");
|
|
43200
43675
|
return;
|
|
@@ -43227,7 +43702,7 @@ ${transcript}`;
|
|
|
43227
43702
|
${caption}` : voiceText : caption;
|
|
43228
43703
|
if (!userText && promptLines.length === 0)
|
|
43229
43704
|
return;
|
|
43230
|
-
runTurn(topic, composeAttachmentPrompt(userText, promptLines), chatId, threadId);
|
|
43705
|
+
runTurn(topic, composeAttachmentPrompt(userText, promptLines), chatId, threadId, msg);
|
|
43231
43706
|
}
|
|
43232
43707
|
function flushGroup(key) {
|
|
43233
43708
|
const entry = mediaGroups.get(key);
|
|
@@ -43268,13 +43743,20 @@ ${caption}` : voiceText : caption;
|
|
|
43268
43743
|
}
|
|
43269
43744
|
async function handleGroupFlush(entry) {
|
|
43270
43745
|
const { messages, chatId, threadId } = entry;
|
|
43746
|
+
const sourceMessage = messages.reduce((earliest, message) => {
|
|
43747
|
+
if (!Number.isSafeInteger(message.message_id))
|
|
43748
|
+
return earliest;
|
|
43749
|
+
if (!earliest || !Number.isSafeInteger(earliest.message_id))
|
|
43750
|
+
return message;
|
|
43751
|
+
return message.message_id < earliest.message_id ? message : earliest;
|
|
43752
|
+
}, undefined) ?? messages[0];
|
|
43271
43753
|
const topic = resolveTopic(chatId, threadId);
|
|
43272
43754
|
const captions = messages.map((message) => (message.text ?? message.caption ?? "").trim()).filter((caption) => caption.length > 0);
|
|
43273
43755
|
if (typeof client.getFileLink !== "function") {
|
|
43274
43756
|
logger.warn({ chatId }, "telegram adapter: media group but client lacks getFileLink \u2014 captions only");
|
|
43275
43757
|
if (captions.length > 0)
|
|
43276
43758
|
runTurn(topic, captions.join(`
|
|
43277
|
-
`), chatId, threadId);
|
|
43759
|
+
`), chatId, threadId, sourceMessage);
|
|
43278
43760
|
else
|
|
43279
43761
|
reply(chatId, threadId, "this bot cannot download attachments");
|
|
43280
43762
|
return;
|
|
@@ -43294,7 +43776,7 @@ ${caption}` : voiceText : caption;
|
|
|
43294
43776
|
`);
|
|
43295
43777
|
if (!userText && promptLines.length === 0)
|
|
43296
43778
|
return;
|
|
43297
|
-
runTurn(topic, composeAttachmentPrompt(userText, promptLines), chatId, threadId);
|
|
43779
|
+
runTurn(topic, composeAttachmentPrompt(userText, promptLines), chatId, threadId, sourceMessage);
|
|
43298
43780
|
}
|
|
43299
43781
|
function stop() {
|
|
43300
43782
|
for (const entry of mediaGroups.values()) {
|
|
@@ -43370,8 +43852,27 @@ function renderOutbound(text2, maxLen = 4096) {
|
|
|
43370
43852
|
}
|
|
43371
43853
|
|
|
43372
43854
|
// ../../adapters/telegram/src/adapter.ts
|
|
43855
|
+
import { randomUUID as randomUUID29 } from "crypto";
|
|
43373
43856
|
import { existsSync as existsSync37 } from "fs";
|
|
43374
43857
|
import { basename as basename13, extname as extname6 } from "path";
|
|
43858
|
+
function telegramGroupScope(chatId) {
|
|
43859
|
+
return `tg:${chatId}`;
|
|
43860
|
+
}
|
|
43861
|
+
function telegramGroupIdFromScope(scope) {
|
|
43862
|
+
if (!scope?.startsWith("tg:"))
|
|
43863
|
+
return null;
|
|
43864
|
+
const chatId = Number(scope.slice(3));
|
|
43865
|
+
return Number.isSafeInteger(chatId) ? chatId : null;
|
|
43866
|
+
}
|
|
43867
|
+
function telegramClientMessageId(msg, chatId, threadId) {
|
|
43868
|
+
const messageId = msg?.message_id;
|
|
43869
|
+
const source = msg?.media_group_id ? Number.isSafeInteger(messageId) ? `album:${msg.media_group_id}:${messageId}` : `album:${msg.media_group_id}:legacy:${randomUUID29()}` : Number.isSafeInteger(messageId) ? `message:${messageId}` : `legacy:${randomUUID29()}`;
|
|
43870
|
+
return `telegram:${chatId}:${threadId ?? "main"}:${source}`;
|
|
43871
|
+
}
|
|
43872
|
+
function telegramActorLabel(msg) {
|
|
43873
|
+
const username = msg?.from?.username?.trim().replace(/^@/, "");
|
|
43874
|
+
return username ? `@${username}` : undefined;
|
|
43875
|
+
}
|
|
43375
43876
|
function startTelegramAdapter(opts) {
|
|
43376
43877
|
const { client, forumChatId } = opts;
|
|
43377
43878
|
const userId = opts.userId ?? "local";
|
|
@@ -43387,19 +43888,44 @@ function startTelegramAdapter(opts) {
|
|
|
43387
43888
|
const sendTimeoutMs = opts.sendTimeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
43388
43889
|
const typingHeartbeatMs = Math.max(250, opts.typingHeartbeatMs ?? 4000);
|
|
43389
43890
|
const footerEnabled = opts.footer === true;
|
|
43390
|
-
const dispatchTurn = opts.startTurn ?? startAiTurn;
|
|
43391
43891
|
const store = openMappingStore(opts.mappingDbPath);
|
|
43392
43892
|
const restoredForumChatId = store.loadForumChatId();
|
|
43393
|
-
|
|
43394
|
-
|
|
43395
|
-
let forumMode = initialForumChatId !== undefined && typeof client.createForumTopic === "function";
|
|
43396
|
-
let forumManageTopicsAvailable = forumMode && forumChatId !== undefined;
|
|
43397
|
-
if (forumChatId !== undefined)
|
|
43893
|
+
if (forumChatId !== undefined) {
|
|
43894
|
+
store.saveGroup({ chatId: forumChatId });
|
|
43398
43895
|
store.saveForumChatId(forumChatId);
|
|
43399
|
-
|
|
43400
|
-
|
|
43896
|
+
}
|
|
43897
|
+
const forumGroups = new Map;
|
|
43898
|
+
for (const group of store.loadGroups()) {
|
|
43899
|
+
forumGroups.set(group.chatId, {
|
|
43900
|
+
chatId: group.chatId,
|
|
43901
|
+
manageTopicsAvailable: group.chatId === forumChatId,
|
|
43902
|
+
configured: group.chatId === forumChatId,
|
|
43903
|
+
permissionRevision: 0
|
|
43904
|
+
});
|
|
43905
|
+
}
|
|
43906
|
+
if (restoredForumChatId !== undefined && !forumGroups.has(restoredForumChatId)) {
|
|
43907
|
+
forumGroups.set(restoredForumChatId, {
|
|
43908
|
+
chatId: restoredForumChatId,
|
|
43909
|
+
manageTopicsAvailable: false,
|
|
43910
|
+
configured: false,
|
|
43911
|
+
permissionRevision: 0
|
|
43912
|
+
});
|
|
43913
|
+
}
|
|
43914
|
+
if (forumGroups.size > 0 && typeof client.createForumTopic !== "function") {
|
|
43915
|
+
logger.warn({ forumChatIds: [...forumGroups.keys()] }, "telegram adapter: forum groups configured but client lacks createForumTopic");
|
|
43401
43916
|
}
|
|
43402
43917
|
const personalGeneral = ensurePersonalGeneral(userId, "telegram");
|
|
43918
|
+
const groupGenerals = new Map;
|
|
43919
|
+
const generalForGroup = (chatId) => {
|
|
43920
|
+
const existing = groupGenerals.get(chatId);
|
|
43921
|
+
if (existing)
|
|
43922
|
+
return existing;
|
|
43923
|
+
const general = ensurePersonalGeneral(userId, "telegram", {
|
|
43924
|
+
surfaceScope: telegramGroupScope(chatId)
|
|
43925
|
+
});
|
|
43926
|
+
groupGenerals.set(chatId, general);
|
|
43927
|
+
return general;
|
|
43928
|
+
};
|
|
43403
43929
|
const byKey = new Map;
|
|
43404
43930
|
const byTopic = new Map;
|
|
43405
43931
|
const targetByQueryId = new Map;
|
|
@@ -43573,6 +44099,22 @@ function startTelegramAdapter(opts) {
|
|
|
43573
44099
|
}
|
|
43574
44100
|
function bindMapping(chatId, threadId, topicId, { persist = true } = {}) {
|
|
43575
44101
|
const key = mappingKey(chatId, threadId);
|
|
44102
|
+
if (forumGroups.has(chatId)) {
|
|
44103
|
+
const topic = getTopic(topicId);
|
|
44104
|
+
const expectedScope = telegramGroupScope(chatId);
|
|
44105
|
+
if (!topic || topic.surface !== "telegram" || (topic.surfaceScope ?? null) !== expectedScope) {
|
|
44106
|
+
throw new Error(`refusing to bind Telegram group ${chatId} to topic ${topicId} outside ${expectedScope}`);
|
|
44107
|
+
}
|
|
44108
|
+
for (const existing of [...byTopic.get(topicId) ?? []]) {
|
|
44109
|
+
if (existing.chatId !== chatId || mappingKey(existing.chatId, existing.threadId) === key) {
|
|
44110
|
+
continue;
|
|
44111
|
+
}
|
|
44112
|
+
detachFromTopic(existing);
|
|
44113
|
+
byKey.delete(mappingKey(existing.chatId, existing.threadId));
|
|
44114
|
+
if (persist)
|
|
44115
|
+
store.deleteByChat(existing.chatId, existing.threadId);
|
|
44116
|
+
}
|
|
44117
|
+
}
|
|
43576
44118
|
const prev = byKey.get(key);
|
|
43577
44119
|
if (prev) {
|
|
43578
44120
|
if (prev.topicId === topicId)
|
|
@@ -43639,7 +44181,8 @@ function startTelegramAdapter(opts) {
|
|
|
43639
44181
|
}
|
|
43640
44182
|
function loadExistingTopic(chatId, topicId, threadId) {
|
|
43641
44183
|
const topic = getTopic(topicId);
|
|
43642
|
-
|
|
44184
|
+
const expectedScope = forumGroups.has(chatId) ? telegramGroupScope(chatId) : null;
|
|
44185
|
+
if (!topic?.participants.some((participant) => participant.userId === userId) || topic.surface !== "telegram" || (topic.surfaceScope ?? null) !== expectedScope) {
|
|
43643
44186
|
return false;
|
|
43644
44187
|
}
|
|
43645
44188
|
bindMapping(chatId, threadId, topic.id);
|
|
@@ -43647,7 +44190,10 @@ function startTelegramAdapter(opts) {
|
|
|
43647
44190
|
}
|
|
43648
44191
|
if (!store.isFlagSet(SURFACE_BACKFILL_FLAG)) {
|
|
43649
44192
|
const mappedIds = [
|
|
43650
|
-
...new Set(store.load().
|
|
44193
|
+
...new Set(store.load().flatMap((mapping) => {
|
|
44194
|
+
const topic = getTopic(mapping.topicId);
|
|
44195
|
+
return topic && topic.kind !== "manager" && topic.participants.some((participant) => participant.userId === userId) ? [topic.id] : [];
|
|
44196
|
+
}))
|
|
43651
44197
|
];
|
|
43652
44198
|
const moved = setTopicSurfaces(mappedIds, "telegram");
|
|
43653
44199
|
store.setFlag(SURFACE_BACKFILL_FLAG);
|
|
@@ -43655,12 +44201,70 @@ function startTelegramAdapter(opts) {
|
|
|
43655
44201
|
logger.info({ moved, mapped: mappedIds.length }, "telegram adapter: moved mapped topics onto the telegram surface");
|
|
43656
44202
|
}
|
|
43657
44203
|
}
|
|
44204
|
+
if (!store.isFlagSet(GROUP_SCOPE_BACKFILL_FLAG)) {
|
|
44205
|
+
const groupIds = new Set(forumGroups.keys());
|
|
44206
|
+
const groupsByTopic = new Map;
|
|
44207
|
+
for (const mapping of store.load()) {
|
|
44208
|
+
if (!groupIds.has(mapping.chatId))
|
|
44209
|
+
continue;
|
|
44210
|
+
const groups = groupsByTopic.get(mapping.topicId) ?? new Set;
|
|
44211
|
+
groups.add(mapping.chatId);
|
|
44212
|
+
groupsByTopic.set(mapping.topicId, groups);
|
|
44213
|
+
}
|
|
44214
|
+
let filed = 0;
|
|
44215
|
+
for (const [topicId, groups] of groupsByTopic) {
|
|
44216
|
+
if (groups.size !== 1) {
|
|
44217
|
+
logger.warn({ topicId, groupIds: [...groups] }, "telegram adapter: legacy topic spans multiple groups; scope migration skipped");
|
|
44218
|
+
continue;
|
|
44219
|
+
}
|
|
44220
|
+
const topic = getTopic(topicId);
|
|
44221
|
+
if (!topic || topic.kind === "manager" || topic.surface !== "telegram" || !topic.participants.some((participant) => participant.userId === userId)) {
|
|
44222
|
+
continue;
|
|
44223
|
+
}
|
|
44224
|
+
const [chatId] = groups;
|
|
44225
|
+
if (chatId !== undefined) {
|
|
44226
|
+
const targetScope = telegramGroupScope(chatId);
|
|
44227
|
+
const currentScope = topic.surfaceScope ?? null;
|
|
44228
|
+
if (currentScope !== null && currentScope !== targetScope) {
|
|
44229
|
+
logger.warn({ topicId, currentScope, targetScope }, "telegram adapter: legacy mapping conflicts with an existing topic namespace");
|
|
44230
|
+
continue;
|
|
44231
|
+
}
|
|
44232
|
+
const titleConflict = findTopicTitleConflict(topic.title, topic.kind ?? "agent", {
|
|
44233
|
+
excludeTopicId: topic.id,
|
|
44234
|
+
surface: "telegram",
|
|
44235
|
+
surfaceScope: targetScope
|
|
44236
|
+
});
|
|
44237
|
+
if (titleConflict) {
|
|
44238
|
+
logger.warn({ topicId, conflictTopicId: titleConflict.id, targetScope }, "telegram adapter: legacy topic title conflicts in target namespace");
|
|
44239
|
+
continue;
|
|
44240
|
+
}
|
|
44241
|
+
filed += Number(setTopicSurfaceScope(topicId, "telegram", targetScope));
|
|
44242
|
+
}
|
|
44243
|
+
}
|
|
44244
|
+
store.setFlag(GROUP_SCOPE_BACKFILL_FLAG);
|
|
44245
|
+
if (filed > 0) {
|
|
44246
|
+
logger.info({ filed }, "telegram adapter: filed legacy topics into group namespaces");
|
|
44247
|
+
}
|
|
44248
|
+
}
|
|
43658
44249
|
for (const persisted of store.load()) {
|
|
43659
44250
|
const topic = getTopic(persisted.topicId);
|
|
43660
|
-
|
|
44251
|
+
const expectedScope = forumGroups.has(persisted.chatId) ? telegramGroupScope(persisted.chatId) : null;
|
|
44252
|
+
if (topic?.participants.some((participant) => participant.userId === userId) && topic.surface === "telegram" && (topic.surfaceScope ?? null) === expectedScope) {
|
|
43661
44253
|
bindMapping(persisted.chatId, persisted.threadId, persisted.topicId, { persist: false });
|
|
43662
44254
|
continue;
|
|
43663
44255
|
}
|
|
44256
|
+
if (topic) {
|
|
44257
|
+
store.deleteByChat(persisted.chatId, persisted.threadId);
|
|
44258
|
+
logger.warn({
|
|
44259
|
+
chatId: persisted.chatId,
|
|
44260
|
+
threadId: persisted.threadId,
|
|
44261
|
+
topicId: persisted.topicId,
|
|
44262
|
+
topicSurface: topic.surface,
|
|
44263
|
+
topicSurfaceScope: topic.surfaceScope ?? null,
|
|
44264
|
+
expectedScope
|
|
44265
|
+
}, "telegram adapter: quarantined mapping outside its topic namespace");
|
|
44266
|
+
continue;
|
|
44267
|
+
}
|
|
43664
44268
|
cleanupPersistedMapping(persisted, "telegram adapter: stale forum thread cleanup failed");
|
|
43665
44269
|
}
|
|
43666
44270
|
function reconcileStaleMappings() {
|
|
@@ -43680,8 +44284,15 @@ function startTelegramAdapter(opts) {
|
|
|
43680
44284
|
suppressMaterialize--;
|
|
43681
44285
|
}
|
|
43682
44286
|
}
|
|
43683
|
-
function getOrCreateTopic(title, agent) {
|
|
43684
|
-
|
|
44287
|
+
function getOrCreateTopic(chatId, title, agent) {
|
|
44288
|
+
const surfaceScope = forumGroups.has(chatId) ? telegramGroupScope(chatId) : null;
|
|
44289
|
+
return getTopicByNameForUser(title, userId, { surface: "telegram", surfaceScope }) ?? registerTopicLocal({
|
|
44290
|
+
title,
|
|
44291
|
+
userId,
|
|
44292
|
+
kind: "agent",
|
|
44293
|
+
surfaceScope,
|
|
44294
|
+
...agent ? { agent } : {}
|
|
44295
|
+
});
|
|
43685
44296
|
}
|
|
43686
44297
|
function resolveMapping(chatId, threadId) {
|
|
43687
44298
|
const cached = byKey.get(mappingKey(chatId, threadId));
|
|
@@ -43691,7 +44302,7 @@ function startTelegramAdapter(opts) {
|
|
|
43691
44302
|
return topic2;
|
|
43692
44303
|
unbindTopic(cached.topicId);
|
|
43693
44304
|
}
|
|
43694
|
-
const topic = getOrCreateTopic(titleFor(chatId, threadId), opts.defaultAgent);
|
|
44305
|
+
const topic = getOrCreateTopic(chatId, titleFor(chatId, threadId), opts.defaultAgent);
|
|
43695
44306
|
bindMapping(chatId, threadId, topic.id);
|
|
43696
44307
|
return topic;
|
|
43697
44308
|
}
|
|
@@ -44009,12 +44620,16 @@ function startTelegramAdapter(opts) {
|
|
|
44009
44620
|
const pendingByTopic = new Map;
|
|
44010
44621
|
const permissionBlockedTopics = new Map;
|
|
44011
44622
|
const materializeTombstones = new Map;
|
|
44012
|
-
if (
|
|
44623
|
+
if (forumGroups.size > 0) {
|
|
44013
44624
|
for (const t of store.loadTombstones())
|
|
44014
44625
|
materializeTombstones.set(t.topicId, t.title);
|
|
44015
44626
|
}
|
|
44016
44627
|
function deliverFallback(topicId, title, payload) {
|
|
44017
|
-
|
|
44628
|
+
const topic = getTopic(topicId);
|
|
44629
|
+
const chatId = telegramGroupIdFromScope(topic?.surfaceScope);
|
|
44630
|
+
if (chatId === null)
|
|
44631
|
+
return Promise.resolve();
|
|
44632
|
+
return deliverToTargets(topicId, [{ chatId, topicId }], {
|
|
44018
44633
|
text: payload.text ? `[${title}] ${payload.text}` : "",
|
|
44019
44634
|
files: payload.files,
|
|
44020
44635
|
runtimeMessageId: payload.runtimeMessageId,
|
|
@@ -44027,16 +44642,18 @@ function startTelegramAdapter(opts) {
|
|
|
44027
44642
|
}
|
|
44028
44643
|
}
|
|
44029
44644
|
function materializeTopic(topic) {
|
|
44030
|
-
if (
|
|
44645
|
+
if (topic.surface !== "telegram" || !topic.participants?.some((participant) => participant.userId === userId)) {
|
|
44031
44646
|
return false;
|
|
44032
44647
|
}
|
|
44033
|
-
|
|
44648
|
+
const materializationChat = telegramGroupIdFromScope(topic.surfaceScope);
|
|
44649
|
+
if (materializationChat === null)
|
|
44034
44650
|
return false;
|
|
44035
|
-
|
|
44651
|
+
const group = forumGroups.get(materializationChat);
|
|
44652
|
+
if (!group?.manageTopicsAvailable || suppressMaterialize > 0 || byTopic.has(topic.id) || pendingByTopic.has(topic.id) || permissionBlockedTopics.has(topic.id) || materializeTombstones.has(topic.id)) {
|
|
44036
44653
|
return false;
|
|
44037
|
-
|
|
44654
|
+
}
|
|
44655
|
+
if (!isTopicVisible(topic))
|
|
44038
44656
|
return false;
|
|
44039
|
-
const materializationChat = forumChat;
|
|
44040
44657
|
const pending2 = { buffer: [], cancelled: false };
|
|
44041
44658
|
pendingByTopic.set(topic.id, pending2);
|
|
44042
44659
|
(async () => {
|
|
@@ -44050,12 +44667,12 @@ function startTelegramAdapter(opts) {
|
|
|
44050
44667
|
return;
|
|
44051
44668
|
const errorInfo = telegramErrorInfo(err2);
|
|
44052
44669
|
if (isManageTopicsPermissionError(errorInfo)) {
|
|
44053
|
-
const permissionWasAvailable =
|
|
44054
|
-
|
|
44670
|
+
const permissionWasAvailable = group.manageTopicsAvailable;
|
|
44671
|
+
group.manageTopicsAvailable = false;
|
|
44055
44672
|
permissionBlockedTopics.set(topic.id, topic.title);
|
|
44056
44673
|
logger.warn({ err: err2, topicId: topic.id, title: topic.title }, "telegram adapter: Manage Topics unavailable \u2014 waiting for permission recovery");
|
|
44057
44674
|
if (permissionWasAvailable) {
|
|
44058
|
-
reply(
|
|
44675
|
+
reply(materializationChat, undefined, 'Forum topic creation is paused. Enable the bot administrator permission "Manage Topics"; pending topics will be retried automatically.');
|
|
44059
44676
|
}
|
|
44060
44677
|
flushBuffered(topic.id, pending2.buffer, (payload) => deliverFallback(topic.id, topic.title, payload));
|
|
44061
44678
|
return;
|
|
@@ -44144,8 +44761,10 @@ function startTelegramAdapter(opts) {
|
|
|
44144
44761
|
const topic = getTopic(topicId);
|
|
44145
44762
|
if (!topic)
|
|
44146
44763
|
return false;
|
|
44147
|
-
|
|
44148
|
-
|
|
44764
|
+
const groupId = telegramGroupIdFromScope(topic.surfaceScope);
|
|
44765
|
+
const group = groupId === null ? undefined : forumGroups.get(groupId);
|
|
44766
|
+
if (group) {
|
|
44767
|
+
if (!group.manageTopicsAvailable || permissionBlockedTopics.has(topicId)) {
|
|
44149
44768
|
enqueueSend(topicId, () => deliverFallback(topicId, topic.title, payload)).then(payload.onSettled);
|
|
44150
44769
|
return true;
|
|
44151
44770
|
}
|
|
@@ -44291,32 +44910,52 @@ ${suffix}`;
|
|
|
44291
44910
|
return;
|
|
44292
44911
|
await client.sendMessage(chatId, onboardingGuide(bot?.username), threadOpts(threadId)).catch((err2) => logger.warn({ err: err2, chatId }, "telegram adapter: onboarding guide failed"));
|
|
44293
44912
|
}
|
|
44294
|
-
function materializeVisibleTopics() {
|
|
44295
|
-
|
|
44913
|
+
function materializeVisibleTopics(chatId) {
|
|
44914
|
+
const group = forumGroups.get(chatId);
|
|
44915
|
+
if (!group?.manageTopicsAvailable)
|
|
44296
44916
|
return;
|
|
44297
|
-
|
|
44917
|
+
const topics = listTopics({
|
|
44918
|
+
surface: "telegram",
|
|
44919
|
+
surfaceScope: telegramGroupScope(chatId)
|
|
44920
|
+
});
|
|
44921
|
+
for (const topic of topics) {
|
|
44922
|
+
if (topic.kind === "manager")
|
|
44923
|
+
continue;
|
|
44298
44924
|
if (isTopicVisible(topic) && topic.participants.some((participant) => participant.userId === userId)) {
|
|
44299
44925
|
materializeTopic(topic);
|
|
44300
44926
|
}
|
|
44301
44927
|
}
|
|
44302
44928
|
}
|
|
44303
|
-
function restoreForumTopicCreation({ retryPermanent = false } = {}) {
|
|
44304
|
-
|
|
44305
|
-
|
|
44929
|
+
function restoreForumTopicCreation(chatId, { retryPermanent = false } = {}) {
|
|
44930
|
+
const group = forumGroups.get(chatId);
|
|
44931
|
+
if (!group)
|
|
44932
|
+
return;
|
|
44933
|
+
group.manageTopicsAvailable = true;
|
|
44934
|
+
for (const topicId of [...permissionBlockedTopics.keys()]) {
|
|
44935
|
+
if (telegramGroupIdFromScope(getTopic(topicId)?.surfaceScope) === chatId) {
|
|
44936
|
+
permissionBlockedTopics.delete(topicId);
|
|
44937
|
+
}
|
|
44938
|
+
}
|
|
44306
44939
|
if (retryPermanent) {
|
|
44307
|
-
materializeTombstones.
|
|
44308
|
-
|
|
44940
|
+
for (const topicId of [...materializeTombstones.keys()]) {
|
|
44941
|
+
if (telegramGroupIdFromScope(getTopic(topicId)?.surfaceScope) === chatId) {
|
|
44942
|
+
materializeTombstones.delete(topicId);
|
|
44943
|
+
store.deleteTombstone(topicId);
|
|
44944
|
+
}
|
|
44945
|
+
}
|
|
44309
44946
|
}
|
|
44310
44947
|
reconcileStaleMappings();
|
|
44311
|
-
materializeVisibleTopics();
|
|
44948
|
+
materializeVisibleTopics(chatId);
|
|
44312
44949
|
}
|
|
44313
44950
|
async function notifyOwnerDms(text2) {
|
|
44314
44951
|
await Promise.allSettled([...ownerDmChatIds].map((chatId) => client.sendMessage(chatId, text2)));
|
|
44315
44952
|
}
|
|
44316
44953
|
async function disconnectForum(chatId) {
|
|
44317
|
-
if (!
|
|
44954
|
+
if (!forumGroups.has(chatId))
|
|
44318
44955
|
return false;
|
|
44319
44956
|
for (const [topicId, pending2] of pendingByTopic) {
|
|
44957
|
+
if (telegramGroupIdFromScope(getTopic(topicId)?.surfaceScope) !== chatId)
|
|
44958
|
+
continue;
|
|
44320
44959
|
pending2.cancelled = true;
|
|
44321
44960
|
if (pendingByTopic.get(topicId) === pending2)
|
|
44322
44961
|
pendingByTopic.delete(topicId);
|
|
@@ -44329,14 +44968,23 @@ ${suffix}`;
|
|
|
44329
44968
|
if (target.chatId === chatId)
|
|
44330
44969
|
targetByQueryId.delete(queryId);
|
|
44331
44970
|
}
|
|
44332
|
-
permissionBlockedTopics.
|
|
44333
|
-
|
|
44334
|
-
|
|
44971
|
+
for (const topicId of [...permissionBlockedTopics.keys()]) {
|
|
44972
|
+
if (telegramGroupIdFromScope(getTopic(topicId)?.surfaceScope) === chatId) {
|
|
44973
|
+
permissionBlockedTopics.delete(topicId);
|
|
44974
|
+
}
|
|
44975
|
+
}
|
|
44976
|
+
for (const topicId of [...materializeTombstones.keys()]) {
|
|
44977
|
+
if (telegramGroupIdFromScope(getTopic(topicId)?.surfaceScope) === chatId) {
|
|
44978
|
+
materializeTombstones.delete(topicId);
|
|
44979
|
+
store.deleteTombstone(topicId);
|
|
44980
|
+
}
|
|
44981
|
+
}
|
|
44335
44982
|
store.outboxDeleteByChat(chatId);
|
|
44336
|
-
store.
|
|
44337
|
-
|
|
44338
|
-
|
|
44339
|
-
|
|
44983
|
+
store.deleteGroup(chatId);
|
|
44984
|
+
if (store.loadForumChatId() === chatId)
|
|
44985
|
+
store.clearForumChatId();
|
|
44986
|
+
forumGroups.delete(chatId);
|
|
44987
|
+
groupGenerals.delete(chatId);
|
|
44340
44988
|
await notifyOwnerDms("The Telegram forum was disconnected because the bot left or was removed. Your Negotium topics were preserved; promote the bot in a forum group to reconnect.");
|
|
44341
44989
|
logger.info({ userId, forumChatId: chatId }, "telegram adapter: forum disconnected");
|
|
44342
44990
|
return true;
|
|
@@ -44346,16 +44994,14 @@ ${suffix}`;
|
|
|
44346
44994
|
reply(ownerTelegramId, undefined, "This Telegram client cannot create forum topics, so the group was not connected.");
|
|
44347
44995
|
return false;
|
|
44348
44996
|
}
|
|
44349
|
-
if (forumMode && forumChat !== chat.id) {
|
|
44350
|
-
reply(ownerTelegramId, undefined, `A different forum group (${forumChat}) is already connected to this Negotium node.`);
|
|
44351
|
-
return false;
|
|
44352
|
-
}
|
|
44353
44997
|
const hasManageTopics = canManageTopics(botMember);
|
|
44354
|
-
|
|
44355
|
-
|
|
44356
|
-
|
|
44998
|
+
const existingGroup = forumGroups.get(chat.id);
|
|
44999
|
+
if (existingGroup) {
|
|
45000
|
+
existingGroup.permissionRevision += 1;
|
|
45001
|
+
const recovered = hasManageTopics && !existingGroup.manageTopicsAvailable;
|
|
45002
|
+
const lostPermission = !hasManageTopics && existingGroup.manageTopicsAvailable;
|
|
44357
45003
|
if (hasManageTopics) {
|
|
44358
|
-
restoreForumTopicCreation({ retryPermanent: recovered });
|
|
45004
|
+
restoreForumTopicCreation(chat.id, { retryPermanent: recovered });
|
|
44359
45005
|
if (recovered) {
|
|
44360
45006
|
await Promise.allSettled([
|
|
44361
45007
|
client.sendMessage(chat.id, "Manage Topics permission confirmed. Pending Negotium topics are being created now."),
|
|
@@ -44363,23 +45009,29 @@ ${suffix}`;
|
|
|
44363
45009
|
]);
|
|
44364
45010
|
}
|
|
44365
45011
|
} else {
|
|
44366
|
-
|
|
45012
|
+
existingGroup.manageTopicsAvailable = false;
|
|
44367
45013
|
if (lostPermission) {
|
|
44368
45014
|
await notifyOwnerDms(`Manage Topics permission was removed from \u201C${chat.title?.trim() || chat.id}\u201D. Existing topics are preserved; restore the permission to resume topic creation.`);
|
|
44369
45015
|
}
|
|
44370
45016
|
}
|
|
44371
45017
|
return true;
|
|
44372
45018
|
}
|
|
44373
|
-
|
|
44374
|
-
|
|
44375
|
-
|
|
44376
|
-
|
|
44377
|
-
|
|
44378
|
-
|
|
44379
|
-
store.
|
|
44380
|
-
|
|
45019
|
+
forumGroups.set(chat.id, {
|
|
45020
|
+
chatId: chat.id,
|
|
45021
|
+
manageTopicsAvailable: hasManageTopics,
|
|
45022
|
+
configured: chat.id === forumChatId,
|
|
45023
|
+
permissionRevision: 1
|
|
45024
|
+
});
|
|
45025
|
+
store.saveGroup({
|
|
45026
|
+
chatId: chat.id,
|
|
45027
|
+
title: chat.title?.trim(),
|
|
45028
|
+
ownerTelegramUserId: ownerTelegramId
|
|
45029
|
+
});
|
|
45030
|
+
if (store.loadForumChatId() === undefined)
|
|
45031
|
+
store.saveForumChatId(chat.id);
|
|
45032
|
+
bindMapping(chat.id, undefined, generalForGroup(chat.id).id);
|
|
44381
45033
|
ownerDmChatIds.add(ownerTelegramId);
|
|
44382
|
-
materializeVisibleTopics();
|
|
45034
|
+
materializeVisibleTopics(chat.id);
|
|
44383
45035
|
const title = chat.title?.trim() || String(chat.id);
|
|
44384
45036
|
const permissionWarning = hasManageTopics ? "" : `
|
|
44385
45037
|
|
|
@@ -44401,9 +45053,8 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44401
45053
|
if (msg.chat.type !== "supergroup" || !msg.chat.is_forum || senderId === undefined || !isAllowed(senderId)) {
|
|
44402
45054
|
return false;
|
|
44403
45055
|
}
|
|
44404
|
-
|
|
44405
|
-
|
|
44406
|
-
if (forumMode && forumManageTopicsAvailable)
|
|
45056
|
+
const connected = forumGroups.get(msg.chat.id);
|
|
45057
|
+
if (connected?.manageTopicsAvailable)
|
|
44407
45058
|
return true;
|
|
44408
45059
|
if (typeof client.getMe !== "function" || typeof client.getChatMember !== "function") {
|
|
44409
45060
|
return false;
|
|
@@ -44419,74 +45070,124 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44419
45070
|
if (!isChatAdmin(botMember) || !isChatAdmin(senderMember))
|
|
44420
45071
|
return false;
|
|
44421
45072
|
await linkForumAndAnnounce(senderId, msg.chat, botMember);
|
|
44422
|
-
return
|
|
45073
|
+
return forumGroups.has(msg.chat.id);
|
|
44423
45074
|
} catch (err2) {
|
|
44424
45075
|
logger.debug({ err: err2, groupId: msg.chat.id, senderId }, "telegram adapter: lazy forum auto-connect check failed");
|
|
44425
45076
|
return false;
|
|
44426
45077
|
}
|
|
44427
45078
|
}
|
|
44428
45079
|
async function verifyInitialForumPermissions() {
|
|
44429
|
-
|
|
44430
|
-
|
|
44431
|
-
if (forumChatId !== undefined)
|
|
45080
|
+
const initialGroups = [...forumGroups.values()];
|
|
45081
|
+
if (initialGroups.length === 0)
|
|
44432
45082
|
return;
|
|
44433
45083
|
if (typeof client.getMe !== "function" || typeof client.getChatMember !== "function") {
|
|
44434
|
-
|
|
45084
|
+
for (const group of initialGroups) {
|
|
45085
|
+
if (forumGroups.get(group.chatId) === group)
|
|
45086
|
+
group.manageTopicsAvailable = true;
|
|
45087
|
+
}
|
|
44435
45088
|
return;
|
|
44436
45089
|
}
|
|
44437
|
-
|
|
44438
|
-
|
|
44439
|
-
|
|
44440
|
-
|
|
44441
|
-
|
|
44442
|
-
|
|
44443
|
-
|
|
44444
|
-
|
|
44445
|
-
|
|
44446
|
-
|
|
44447
|
-
const
|
|
44448
|
-
|
|
44449
|
-
|
|
44450
|
-
|
|
45090
|
+
const bot = await resolveBotIdentity();
|
|
45091
|
+
if (!bot || stopped)
|
|
45092
|
+
return;
|
|
45093
|
+
for (const group of initialGroups) {
|
|
45094
|
+
if (forumGroups.get(group.chatId) !== group)
|
|
45095
|
+
continue;
|
|
45096
|
+
if (group.configured)
|
|
45097
|
+
continue;
|
|
45098
|
+
try {
|
|
45099
|
+
const permissionRevision = group.permissionRevision;
|
|
45100
|
+
const member = await client.getChatMember(group.chatId, bot.id);
|
|
45101
|
+
if (forumGroups.get(group.chatId) !== group || group.permissionRevision !== permissionRevision) {
|
|
45102
|
+
continue;
|
|
45103
|
+
}
|
|
45104
|
+
if (member.status === "left" || member.status === "kicked") {
|
|
45105
|
+
await disconnectForum(group.chatId);
|
|
45106
|
+
continue;
|
|
45107
|
+
}
|
|
45108
|
+
if (canManageTopics(member)) {
|
|
45109
|
+
restoreForumTopicCreation(group.chatId, { retryPermanent: !group.configured });
|
|
45110
|
+
} else {
|
|
45111
|
+
group.manageTopicsAvailable = false;
|
|
45112
|
+
}
|
|
45113
|
+
} catch (err2) {
|
|
45114
|
+
logger.warn({ err: err2, forumChatId: group.chatId }, "telegram adapter: initial forum permission check failed");
|
|
44451
45115
|
}
|
|
44452
|
-
} catch (err2) {
|
|
44453
|
-
logger.warn({ err: err2, forumChatId: forumChat }, "telegram adapter: initial forum permission check failed");
|
|
44454
45116
|
}
|
|
44455
45117
|
}
|
|
44456
45118
|
verifyInitialForumPermissions();
|
|
44457
|
-
function runTurn(topic, prompt, chatId, threadId) {
|
|
45119
|
+
function runTurn(topic, prompt, chatId, threadId, sourceMessage) {
|
|
44458
45120
|
const target = {
|
|
44459
45121
|
topicId: topic.id,
|
|
44460
45122
|
chatId,
|
|
44461
45123
|
...threadId !== undefined ? { threadId } : {}
|
|
44462
45124
|
};
|
|
44463
|
-
const rememberTarget = (
|
|
44464
|
-
targetByQueryId.set(
|
|
45125
|
+
const rememberTarget = (queryId) => {
|
|
45126
|
+
targetByQueryId.set(queryId, target);
|
|
44465
45127
|
};
|
|
45128
|
+
const actorLabel = telegramActorLabel(sourceMessage);
|
|
44466
45129
|
const input = {
|
|
44467
45130
|
topic,
|
|
44468
45131
|
userId,
|
|
45132
|
+
clientMessageId: telegramClientMessageId(sourceMessage, chatId, threadId),
|
|
45133
|
+
...actorLabel ? { actorLabel } : {},
|
|
44469
45134
|
text: prompt,
|
|
44470
45135
|
sourceAdapter: "telegram",
|
|
44471
45136
|
visualTools: false,
|
|
44472
45137
|
fileDeliveryTools: true
|
|
44473
45138
|
};
|
|
45139
|
+
const provisionalQueryId = input.clientMessageId;
|
|
45140
|
+
rememberTarget(provisionalQueryId);
|
|
45141
|
+
const adoptQueryId = (queryId) => {
|
|
45142
|
+
if (!queryId)
|
|
45143
|
+
return;
|
|
45144
|
+
if (queryId !== provisionalQueryId && targetByQueryId.get(provisionalQueryId) === target) {
|
|
45145
|
+
targetByQueryId.delete(provisionalQueryId);
|
|
45146
|
+
}
|
|
45147
|
+
rememberTarget(queryId);
|
|
45148
|
+
};
|
|
45149
|
+
const removeProvisionalTarget = () => {
|
|
45150
|
+
if (targetByQueryId.get(provisionalQueryId) === target) {
|
|
45151
|
+
targetByQueryId.delete(provisionalQueryId);
|
|
45152
|
+
}
|
|
45153
|
+
};
|
|
44474
45154
|
if (opts.submitTurn) {
|
|
44475
|
-
opts.submitTurn(input).then(({ queryId
|
|
44476
|
-
|
|
44477
|
-
rememberTarget(queryId2);
|
|
45155
|
+
opts.submitTurn(input).then(({ queryId }) => {
|
|
45156
|
+
adoptQueryId(queryId ?? provisionalQueryId);
|
|
44478
45157
|
}).catch((error2) => {
|
|
45158
|
+
const mayHaveCommitted = error2 instanceof RuntimeGatewayError && (error2.kind === "transport" || error2.kind === "timeout" || error2.kind === "http" && (error2.status ?? 0) >= 500);
|
|
45159
|
+
if (mayHaveCommitted) {
|
|
45160
|
+
const timer = setTimeout(removeProvisionalTarget, 60 * 60000);
|
|
45161
|
+
timer.unref?.();
|
|
45162
|
+
} else {
|
|
45163
|
+
removeProvisionalTarget();
|
|
45164
|
+
}
|
|
44479
45165
|
logger.error({ err: error2, topicId: topic.id }, "telegram adapter: remote turn failed");
|
|
44480
45166
|
});
|
|
44481
45167
|
return;
|
|
44482
45168
|
}
|
|
44483
|
-
|
|
44484
|
-
|
|
44485
|
-
|
|
44486
|
-
|
|
44487
|
-
|
|
44488
|
-
|
|
44489
|
-
|
|
45169
|
+
if (opts.startTurn) {
|
|
45170
|
+
const { queryId } = submitUserMessage({
|
|
45171
|
+
...input,
|
|
45172
|
+
onDispatched: adoptQueryId,
|
|
45173
|
+
startTurn: opts.startTurn
|
|
45174
|
+
});
|
|
45175
|
+
if (queryId)
|
|
45176
|
+
adoptQueryId(queryId);
|
|
45177
|
+
else
|
|
45178
|
+
removeProvisionalTarget();
|
|
45179
|
+
return;
|
|
45180
|
+
}
|
|
45181
|
+
try {
|
|
45182
|
+
const submission = submitRuntimeGatewayTurn({
|
|
45183
|
+
...input,
|
|
45184
|
+
requestId: input.clientMessageId
|
|
45185
|
+
});
|
|
45186
|
+
adoptQueryId(submission.requestId);
|
|
45187
|
+
} catch (error2) {
|
|
45188
|
+
removeProvisionalTarget();
|
|
45189
|
+
throw error2;
|
|
45190
|
+
}
|
|
44490
45191
|
}
|
|
44491
45192
|
const mediaIntake = createTelegramMediaIntake({
|
|
44492
45193
|
client,
|
|
@@ -44502,7 +45203,8 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44502
45203
|
const handleCommand = createTelegramCommandRouter({
|
|
44503
45204
|
userId,
|
|
44504
45205
|
defaultAgent: opts.defaultAgent,
|
|
44505
|
-
|
|
45206
|
+
surfaceScopeFor: (chatId) => forumGroups.has(chatId) ? telegramGroupScope(chatId) : null,
|
|
45207
|
+
isForumGeneral: (chatId, threadId) => forumGroups.has(chatId) && threadId === undefined,
|
|
44506
45208
|
resolveBotUsername: async () => (await resolveBotIdentity())?.username,
|
|
44507
45209
|
isVaultOwner,
|
|
44508
45210
|
reply,
|
|
@@ -44528,8 +45230,10 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44528
45230
|
const firstPrivateContact = privateDm && !byKey.has(mappingKey(chatId));
|
|
44529
45231
|
if (privateDm)
|
|
44530
45232
|
ownerDmChatIds.add(chatId);
|
|
44531
|
-
if (privateDm
|
|
45233
|
+
if (privateDm) {
|
|
44532
45234
|
bindMapping(chatId, threadId, personalGeneral.id);
|
|
45235
|
+
} else if (forumGroups.has(chatId) && threadId === undefined) {
|
|
45236
|
+
bindMapping(chatId, threadId, generalForGroup(chatId).id);
|
|
44533
45237
|
}
|
|
44534
45238
|
if (firstPrivateContact && text2 !== "/start") {
|
|
44535
45239
|
sendOnboardingGuide(chatId);
|
|
@@ -44553,7 +45257,7 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44553
45257
|
}
|
|
44554
45258
|
if (!text2)
|
|
44555
45259
|
return;
|
|
44556
|
-
mediaIntake.enqueue(chatId, threadId, () => runTurn(resolveMapping(chatId, threadId), text2, chatId, threadId));
|
|
45260
|
+
mediaIntake.enqueue(chatId, threadId, () => runTurn(resolveMapping(chatId, threadId), text2, chatId, threadId, msg));
|
|
44557
45261
|
}
|
|
44558
45262
|
client.on("message", (msg) => {
|
|
44559
45263
|
if (stopped)
|
|
@@ -44561,9 +45265,7 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44561
45265
|
if (!isAllowed(msg.from?.id))
|
|
44562
45266
|
return;
|
|
44563
45267
|
if (msg.chat.type === "supergroup" && msg.chat.is_forum) {
|
|
44564
|
-
if (
|
|
44565
|
-
return;
|
|
44566
|
-
if (!forumMode || !forumManageTopicsAvailable) {
|
|
45268
|
+
if (!forumGroups.get(msg.chat.id)?.manageTopicsAvailable) {
|
|
44567
45269
|
tryAutoConnectFromMessage(msg).then((connected) => {
|
|
44568
45270
|
if (connected && !stopped)
|
|
44569
45271
|
handleIncomingMessage(msg);
|
|
@@ -44577,26 +45279,29 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44577
45279
|
if (stopped || update.chat.type !== "supergroup" || !update.chat.is_forum)
|
|
44578
45280
|
return;
|
|
44579
45281
|
const status = update.new_chat_member?.status;
|
|
44580
|
-
if ((status === "left" || status === "kicked") &&
|
|
45282
|
+
if ((status === "left" || status === "kicked") && forumGroups.has(update.chat.id)) {
|
|
44581
45283
|
disconnectForum(update.chat.id).catch((err2) => logger.warn({ err: err2, groupId: update.chat.id }, "telegram adapter: forum disconnect failed"));
|
|
44582
45284
|
return;
|
|
44583
45285
|
}
|
|
44584
|
-
if (
|
|
44585
|
-
const
|
|
44586
|
-
|
|
45286
|
+
if (forumGroups.has(update.chat.id) && status !== "administrator" && status !== "creator") {
|
|
45287
|
+
const group = forumGroups.get(update.chat.id);
|
|
45288
|
+
group.permissionRevision += 1;
|
|
45289
|
+
const permissionWasAvailable = group.manageTopicsAvailable;
|
|
45290
|
+
group.manageTopicsAvailable = false;
|
|
44587
45291
|
if (permissionWasAvailable) {
|
|
44588
45292
|
notifyOwnerDms('The bot no longer has forum administrator access. Existing topics are preserved; restore administrator + "Manage Topics" to resume topic creation.');
|
|
44589
45293
|
}
|
|
44590
45294
|
return;
|
|
44591
45295
|
}
|
|
44592
|
-
if (status !== "administrator" && status !== "creator" || update.from?.id === undefined || !isAllowed(update.from.id) && (
|
|
45296
|
+
if (status !== "administrator" && status !== "creator" || update.from?.id === undefined || !isAllowed(update.from.id) && !forumGroups.has(update.chat.id)) {
|
|
44593
45297
|
return;
|
|
44594
45298
|
}
|
|
44595
45299
|
linkForumAndAnnounce(update.from.id, update.chat, update.new_chat_member).catch((err2) => logger.warn({ err: err2, groupId: update.chat.id }, "telegram adapter: forum auto-connect failed"));
|
|
44596
45300
|
});
|
|
44597
45301
|
async function deliverPersistedRuntimeMessage(event) {
|
|
44598
45302
|
const msg = event.payload;
|
|
44599
|
-
|
|
45303
|
+
const isTelegramOrigin = msg.sourceAdapter === "telegram" || msg.sourceAdapter === "runtime-gateway" && msg.sourceMessageId?.startsWith("telegram:");
|
|
45304
|
+
if (msg.authorId === userId && isTelegramOrigin)
|
|
44600
45305
|
return true;
|
|
44601
45306
|
if (msg.kind === "tool")
|
|
44602
45307
|
return true;
|
|
@@ -44704,7 +45409,7 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44704
45409
|
const unsubscribe = runtimeBus().subscribe((event) => {
|
|
44705
45410
|
if (stopped)
|
|
44706
45411
|
return;
|
|
44707
|
-
if (event.type === "topic-created"
|
|
45412
|
+
if (event.type === "topic-created") {
|
|
44708
45413
|
materializeTopic(event.payload);
|
|
44709
45414
|
return;
|
|
44710
45415
|
}
|
|
@@ -44755,9 +45460,9 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44755
45460
|
pollRuntimeEvents();
|
|
44756
45461
|
const runtimeEventPollTimer = setInterval(pollRuntimeEvents, 100);
|
|
44757
45462
|
runtimeEventPollTimer.unref?.();
|
|
44758
|
-
|
|
44759
|
-
bindMapping(
|
|
44760
|
-
materializeVisibleTopics();
|
|
45463
|
+
for (const group of forumGroups.values()) {
|
|
45464
|
+
bindMapping(group.chatId, undefined, generalForGroup(group.chatId).id);
|
|
45465
|
+
materializeVisibleTopics(group.chatId);
|
|
44761
45466
|
}
|
|
44762
45467
|
return {
|
|
44763
45468
|
name: "telegram",
|
|
@@ -44780,9 +45485,10 @@ Warning: enable the bot administrator permission "Manage Topics" before creating
|
|
|
44780
45485
|
}
|
|
44781
45486
|
};
|
|
44782
45487
|
}
|
|
44783
|
-
var SURFACE_BACKFILL_FLAG = "surface_backfill_20260808", FORUM_TOPIC_NAME_MAX = 128, MAX_PARENT_HOPS = 5, DEFAULT_SEND_TIMEOUT_MS = 60000, PHOTO_EXTS;
|
|
45488
|
+
var SURFACE_BACKFILL_FLAG = "surface_backfill_20260808", GROUP_SCOPE_BACKFILL_FLAG = "telegram_group_scope_backfill_20260829", FORUM_TOPIC_NAME_MAX = 128, MAX_PARENT_HOPS = 5, DEFAULT_SEND_TIMEOUT_MS = 60000, PHOTO_EXTS;
|
|
44784
45489
|
var init_adapter = __esm(async () => {
|
|
44785
45490
|
await init_src();
|
|
45491
|
+
init_runtime_gateway();
|
|
44786
45492
|
await init_commands2();
|
|
44787
45493
|
await init_mapping_store();
|
|
44788
45494
|
await init_media_intake();
|
|
@@ -44814,11 +45520,40 @@ var init_src7 = __esm(async () => {
|
|
|
44814
45520
|
// ../../adapters/telegram/src/cli.ts
|
|
44815
45521
|
var exports_cli2 = {};
|
|
44816
45522
|
__export(exports_cli2, {
|
|
45523
|
+
submitTelegramTurnToNode: () => submitTelegramTurnToNode,
|
|
44817
45524
|
startTelegramFromEnv: () => startTelegramFromEnv,
|
|
44818
45525
|
runTelegramCli: () => runTelegramCli,
|
|
44819
45526
|
installTelegramNodeFileHooks: () => installTelegramNodeFileHooks
|
|
44820
45527
|
});
|
|
44821
45528
|
import TelegramBot from "node-telegram-bot-api";
|
|
45529
|
+
async function submitTelegramTurnToNode(node, input, fetchRequest) {
|
|
45530
|
+
const gateway = new RuntimeGatewayClient({
|
|
45531
|
+
baseUrl: node.baseUrl,
|
|
45532
|
+
token: node.token,
|
|
45533
|
+
...fetchRequest ? { fetch: fetchRequest } : {}
|
|
45534
|
+
});
|
|
45535
|
+
const turn = {
|
|
45536
|
+
topicId: input.topic.id,
|
|
45537
|
+
userId: input.userId,
|
|
45538
|
+
...input.actorLabel ? { actorLabel: input.actorLabel } : {},
|
|
45539
|
+
sourceAdapter: input.sourceAdapter,
|
|
45540
|
+
text: input.text,
|
|
45541
|
+
clientMessageId: input.clientMessageId,
|
|
45542
|
+
requestId: input.clientMessageId,
|
|
45543
|
+
visualTools: input.visualTools,
|
|
45544
|
+
fileDeliveryTools: input.fileDeliveryTools
|
|
45545
|
+
};
|
|
45546
|
+
let acknowledgement;
|
|
45547
|
+
try {
|
|
45548
|
+
acknowledgement = await gateway.submitTurn(turn);
|
|
45549
|
+
} catch (error2) {
|
|
45550
|
+
const retryable = error2 instanceof RuntimeGatewayError && (error2.kind === "transport" || error2.kind === "timeout" || error2.kind === "http" && (error2.status ?? 0) >= 500);
|
|
45551
|
+
if (!retryable)
|
|
45552
|
+
throw error2;
|
|
45553
|
+
acknowledgement = await gateway.submitTurn(turn);
|
|
45554
|
+
}
|
|
45555
|
+
return { queryId: acknowledgement.requestId };
|
|
45556
|
+
}
|
|
44822
45557
|
function installTelegramNodeFileHooks() {
|
|
44823
45558
|
setFileHooks(nodeFileStore.hooks);
|
|
44824
45559
|
}
|
|
@@ -44920,24 +45655,7 @@ async function runTelegramCli(args = process.argv.slice(2)) {
|
|
|
44920
45655
|
},
|
|
44921
45656
|
submitTurn: async (input) => {
|
|
44922
45657
|
const node = await waitForNodeDaemon(1500);
|
|
44923
|
-
|
|
44924
|
-
method: "POST",
|
|
44925
|
-
headers: {
|
|
44926
|
-
authorization: `Bearer ${NODE_CONTROL_TOKEN}`,
|
|
44927
|
-
"content-type": "application/json"
|
|
44928
|
-
},
|
|
44929
|
-
body: JSON.stringify({
|
|
44930
|
-
userId: input.userId,
|
|
44931
|
-
text: input.text,
|
|
44932
|
-
sourceAdapter: input.sourceAdapter,
|
|
44933
|
-
visualTools: input.visualTools,
|
|
44934
|
-
fileDeliveryTools: input.fileDeliveryTools
|
|
44935
|
-
})
|
|
44936
|
-
});
|
|
44937
|
-
const body = await response.json();
|
|
44938
|
-
if (!response.ok)
|
|
44939
|
-
throw new Error(body.error ?? `node returned HTTP ${response.status}`);
|
|
44940
|
-
return { queryId: body.queryId };
|
|
45658
|
+
return submitTelegramTurnToNode(node, input);
|
|
44941
45659
|
}
|
|
44942
45660
|
});
|
|
44943
45661
|
} catch (error2) {
|
|
@@ -44957,6 +45675,7 @@ async function runTelegramCli(args = process.argv.slice(2)) {
|
|
|
44957
45675
|
}
|
|
44958
45676
|
var init_cli2 = __esm(async () => {
|
|
44959
45677
|
await init_src();
|
|
45678
|
+
init_runtime_gateway();
|
|
44960
45679
|
await init_src5();
|
|
44961
45680
|
init_env_auth();
|
|
44962
45681
|
await init_src7();
|
|
@@ -45171,7 +45890,7 @@ var init_protocol = __esm(() => {
|
|
|
45171
45890
|
});
|
|
45172
45891
|
|
|
45173
45892
|
// ../../adapters/otium/src/canonical-mcp-bridge.ts
|
|
45174
|
-
import { randomUUID as
|
|
45893
|
+
import { randomUUID as randomUUID30 } from "crypto";
|
|
45175
45894
|
function readAuthorization(request) {
|
|
45176
45895
|
const value = request.headers.get("authorization");
|
|
45177
45896
|
return value?.startsWith("Bearer ") ? value.slice(7) : null;
|
|
@@ -45312,7 +46031,7 @@ function startCanonicalMcpBridge(options = {}) {
|
|
|
45312
46031
|
const url = `http://127.0.0.1:${server.port}/`;
|
|
45313
46032
|
const unregister = registerCanonicalMcpBridgeEnvProvider((scope) => {
|
|
45314
46033
|
sweep();
|
|
45315
|
-
const token = `${
|
|
46034
|
+
const token = `${randomUUID30()}${randomUUID30()}`;
|
|
45316
46035
|
capabilities.set(token, {
|
|
45317
46036
|
surface: scope.surface,
|
|
45318
46037
|
userId: scope.userId,
|
|
@@ -45422,7 +46141,7 @@ __export(exports_join, {
|
|
|
45422
46141
|
joinCredentialDigest: () => joinCredentialDigest,
|
|
45423
46142
|
isJoinPersisted: () => isJoinPersisted
|
|
45424
46143
|
});
|
|
45425
|
-
import { createHash as createHash12, randomUUID as
|
|
46144
|
+
import { createHash as createHash12, randomUUID as randomUUID31 } from "crypto";
|
|
45426
46145
|
import {
|
|
45427
46146
|
chmodSync as chmodSync7,
|
|
45428
46147
|
closeSync as closeSync5,
|
|
@@ -45511,7 +46230,7 @@ function processIsAlive(pid) {
|
|
|
45511
46230
|
function withJoinCredentialLock(operation) {
|
|
45512
46231
|
const lockPath = joinLockPath();
|
|
45513
46232
|
const ownerPath = resolve25(lockPath, "owner.json");
|
|
45514
|
-
const owner = { pid: process.pid, token:
|
|
46233
|
+
const owner = { pid: process.pid, token: randomUUID31() };
|
|
45515
46234
|
mkdirSync34(dirname23(lockPath), { recursive: true });
|
|
45516
46235
|
for (let attempt = 0;; attempt += 1) {
|
|
45517
46236
|
let created = false;
|
|
@@ -45552,7 +46271,7 @@ function withJoinCredentialLock(operation) {
|
|
|
45552
46271
|
if (attempt > 0) {
|
|
45553
46272
|
throw new Error(`could not recover stale Otium join credential lock at ${lockPath}`);
|
|
45554
46273
|
}
|
|
45555
|
-
const stalePath = `${lockPath}.stale.${process.pid}.${
|
|
46274
|
+
const stalePath = `${lockPath}.stale.${process.pid}.${randomUUID31()}`;
|
|
45556
46275
|
try {
|
|
45557
46276
|
renameSync17(lockPath, stalePath);
|
|
45558
46277
|
rmSync11(stalePath, { recursive: true, force: true });
|
|
@@ -45595,10 +46314,10 @@ function readPersistedJoins(path = joinFilePath()) {
|
|
|
45595
46314
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
45596
46315
|
throw new Error("persisted join credentials are not a JSON object");
|
|
45597
46316
|
}
|
|
45598
|
-
const
|
|
45599
|
-
if (!Array.isArray(
|
|
45600
|
-
return [normalizeJoin(
|
|
45601
|
-
return
|
|
46317
|
+
const record2 = parsed;
|
|
46318
|
+
if (!Array.isArray(record2.joins))
|
|
46319
|
+
return [normalizeJoin(record2)];
|
|
46320
|
+
return record2.joins.map((entry) => {
|
|
45602
46321
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
45603
46322
|
throw new Error("persisted join credentials contain a non-object entry");
|
|
45604
46323
|
}
|
|
@@ -45624,7 +46343,7 @@ function fsyncPath(target) {
|
|
|
45624
46343
|
function writeJoins(joins, allowOverwrite) {
|
|
45625
46344
|
const path = joinFilePath();
|
|
45626
46345
|
const directory = dirname23(path);
|
|
45627
|
-
const temporaryPath = resolve25(directory, `.otium-join.json.${process.pid}.${
|
|
46346
|
+
const temporaryPath = resolve25(directory, `.otium-join.json.${process.pid}.${randomUUID31()}.tmp`);
|
|
45628
46347
|
const payload = { v: JOIN_FILE_VERSION, joins };
|
|
45629
46348
|
let fd;
|
|
45630
46349
|
try {
|
|
@@ -45749,7 +46468,7 @@ var init_join = __esm(async () => {
|
|
|
45749
46468
|
});
|
|
45750
46469
|
|
|
45751
46470
|
// ../../adapters/otium/src/peer-files.ts
|
|
45752
|
-
import { randomUUID as
|
|
46471
|
+
import { randomUUID as randomUUID32 } from "crypto";
|
|
45753
46472
|
import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync35, rmSync as rmSync12, statSync as statSync21 } from "fs";
|
|
45754
46473
|
import { basename as basename14, extname as extname7, join as join47 } from "path";
|
|
45755
46474
|
function fileType(mimeType) {
|
|
@@ -45815,7 +46534,7 @@ function recordFile(args) {
|
|
|
45815
46534
|
});
|
|
45816
46535
|
}
|
|
45817
46536
|
function insertLocalFile(args) {
|
|
45818
|
-
const id =
|
|
46537
|
+
const id = randomUUID32();
|
|
45819
46538
|
mkdirSync35(PEER_FILES_DIR, { recursive: true });
|
|
45820
46539
|
const path = join47(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
|
|
45821
46540
|
copyFileSync5(args.sourcePath, path);
|
|
@@ -45879,7 +46598,7 @@ var init_peer_files = __esm(async () => {
|
|
|
45879
46598
|
});
|
|
45880
46599
|
|
|
45881
46600
|
// ../../adapters/otium/src/runtime-bridge.ts
|
|
45882
|
-
import { randomUUID as
|
|
46601
|
+
import { randomUUID as randomUUID33 } from "crypto";
|
|
45883
46602
|
function isMcpToolResult(value) {
|
|
45884
46603
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
45885
46604
|
return false;
|
|
@@ -45941,7 +46660,7 @@ var init_runtime_bridge = __esm(async () => {
|
|
|
45941
46660
|
} catch (error2) {
|
|
45942
46661
|
return errorResult(`Error: Failed to open hub question: ${error2.message}`);
|
|
45943
46662
|
}
|
|
45944
|
-
const bridgeRequestId = `bridge-ask-${
|
|
46663
|
+
const bridgeRequestId = `bridge-ask-${randomUUID33()}`;
|
|
45945
46664
|
const baseUrl = hubNode.baseUrl.replace(/\/+$/, "");
|
|
45946
46665
|
const post = async (path, body) => {
|
|
45947
46666
|
const response = await fetch(`${baseUrl}${path}`, {
|
|
@@ -46290,10 +47009,10 @@ function readCache() {
|
|
|
46290
47009
|
}
|
|
46291
47010
|
}
|
|
46292
47011
|
function cachedSurfaceScope(join48) {
|
|
46293
|
-
const
|
|
46294
|
-
if (!
|
|
47012
|
+
const record2 = readCache()[join48.cellId];
|
|
47013
|
+
if (!record2 || record2.central !== join48.central || !record2.workspaceId)
|
|
46295
47014
|
return null;
|
|
46296
|
-
return surfaceScopeFor(
|
|
47015
|
+
return surfaceScopeFor(record2.central, record2.workspaceId);
|
|
46297
47016
|
}
|
|
46298
47017
|
function cacheSurfaceScope(join48, workspaceId) {
|
|
46299
47018
|
const scope = surfaceScopeFor(join48.central, workspaceId);
|
|
@@ -47422,7 +48141,7 @@ import {
|
|
|
47422
48141
|
diffieHellman,
|
|
47423
48142
|
generateKeyPairSync,
|
|
47424
48143
|
hkdfSync,
|
|
47425
|
-
randomUUID as
|
|
48144
|
+
randomUUID as randomUUID34
|
|
47426
48145
|
} from "crypto";
|
|
47427
48146
|
import {
|
|
47428
48147
|
chmodSync as chmodSync8,
|
|
@@ -47482,13 +48201,13 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
47482
48201
|
const pair = generateKeyPairSync("x25519");
|
|
47483
48202
|
const pending2 = {
|
|
47484
48203
|
...invite,
|
|
47485
|
-
idempotencyKey:
|
|
48204
|
+
idempotencyKey: randomUUID34(),
|
|
47486
48205
|
privateKey: pair.privateKey.export({ format: "der", type: "pkcs8" }).toString("base64url"),
|
|
47487
48206
|
publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
|
|
47488
48207
|
...nodeName ? { nodeName } : {}
|
|
47489
48208
|
};
|
|
47490
48209
|
mkdirSync37(dirname25(path), { recursive: true });
|
|
47491
|
-
const temporaryPath = resolve27(dirname25(path), `.otium-enrollment-pending.json.${process.pid}.${
|
|
48210
|
+
const temporaryPath = resolve27(dirname25(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID34()}.tmp`);
|
|
47492
48211
|
let fd;
|
|
47493
48212
|
try {
|
|
47494
48213
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
@@ -47523,7 +48242,7 @@ function loadOrCreatePending(invite, nodeName) {
|
|
|
47523
48242
|
function replacePendingEnrollment(pending2) {
|
|
47524
48243
|
const path = pendingEnrollmentPath();
|
|
47525
48244
|
const directory = dirname25(path);
|
|
47526
|
-
const temporaryPath = resolve27(directory, `.otium-enrollment-pending.json.${process.pid}.${
|
|
48245
|
+
const temporaryPath = resolve27(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID34()}.tmp`);
|
|
47527
48246
|
let fd;
|
|
47528
48247
|
try {
|
|
47529
48248
|
fd = openSync6(temporaryPath, "wx", 384);
|
|
@@ -49743,4 +50462,4 @@ switch (command) {
|
|
|
49743
50462
|
}
|
|
49744
50463
|
}
|
|
49745
50464
|
|
|
49746
|
-
//# debugId=
|
|
50465
|
+
//# debugId=5392FFAC60CD6CD864756E2164756E21
|