@segmentstream/agent 0.13.0 → 0.14.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.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/dist/_chunks/{build-metadata-BLeBanNg.mjs → build-metadata-BB2uSGsI.mjs} +1 -1
  3. package/dist/_chunks/principal-VFQWqe5S.mjs +126 -0
  4. package/dist/extension/config.d.ts +5 -0
  5. package/dist/extension/config.mjs +30 -1
  6. package/dist/extension/hooks/authorization.mjs +2 -2
  7. package/dist/extension/lib/attachment-tools.mjs +1 -1
  8. package/dist/extension/lib/attachments.d.ts +2 -0
  9. package/dist/extension/lib/attachments.mjs +1 -0
  10. package/dist/extension/lib/build-metadata.mjs +1 -1
  11. package/dist/extension/lib/confirmations.mjs +1 -1
  12. package/dist/extension/lib/conversation-runtime.mjs +1 -1
  13. package/dist/extension/lib/deferred-confirmations.d.ts +2 -0
  14. package/dist/extension/lib/deferred-confirmations.mjs +21 -10
  15. package/dist/extension/lib/ontology-connection.d.ts +1 -0
  16. package/dist/extension/lib/ontology-connection.mjs +18 -12
  17. package/dist/extension/lib/ontology-gateway.mjs +9 -2
  18. package/dist/extension/lib/ontology-metadata-read.mjs +1 -1
  19. package/dist/extension/lib/ontology-tools.d.ts +9 -2
  20. package/dist/extension/lib/ontology-tools.mjs +28 -4
  21. package/dist/extension/lib/runtime-envelope.d.ts +28 -0
  22. package/dist/extension/lib/runtime-envelope.mjs +8 -0
  23. package/dist/extension/lib/stored-files.mjs +1 -1
  24. package/dist/extension/lib/telegram/adapter.mjs +21 -6
  25. package/dist/extension/lib/telegram/attachments-send.mjs +2 -2
  26. package/dist/extension/lib/telegram/callbacks.d.ts +18 -0
  27. package/dist/extension/lib/telegram/callbacks.mjs +155 -0
  28. package/dist/extension/lib/telegram/confirmation-expiry.mjs +2 -2
  29. package/dist/extension/lib/telegram/confirmations.mjs +4 -4
  30. package/dist/extension/lib/telegram/conversations.mjs +1 -1
  31. package/dist/extension/lib/telegram/deferred-confirmations.mjs +4 -4
  32. package/dist/extension/lib/telegram/files.mjs +1 -1
  33. package/dist/extension/lib/telegram/health.mjs +1 -1
  34. package/dist/extension/lib/telegram/inbound.mjs +8 -4
  35. package/dist/extension/lib/telegram/lifecycle.mjs +14 -2
  36. package/dist/extension/lib/telegram/principal.d.ts +1 -0
  37. package/dist/extension/lib/telegram/principal.mjs +1 -49
  38. package/dist/extension/lib/telegram/sessions.d.ts +3 -1
  39. package/dist/extension/lib/telegram/silent.mjs +2 -2
  40. package/dist/extension/lib/telegram/turn-errors.mjs +1 -1
  41. package/dist/extension/lib/telegram/workspace-action.d.ts +21 -0
  42. package/dist/extension/lib/telegram/workspace-action.mjs +23 -0
  43. package/dist/extension/model.mjs +1 -1
  44. package/dist/extension/tools/attachments_promote.mjs +6 -0
  45. package/dist/extension/tools/ontology.mjs +1 -1
  46. package/dist/extension/tools/questions_ask.mjs +1 -1
  47. package/package.json +2 -2
package/README.md CHANGED
@@ -549,7 +549,7 @@ Eve resolution, so member/topic routing survives a cold start. No second provide
549
549
 
550
550
  ## Release and migration
551
551
 
552
- The coordinated release uses Agent SDK 0.13.0, Eve 0.76.3, Ontology SDK 0.49.0 and App SDK 0.24.1.
552
+ The coordinated release uses Agent SDK 0.14.0, Eve 0.76.3, Ontology SDK 0.50.0 and App SDK 0.24.1.
553
553
  The compatible Cloud archive/API must be deployed first; migrations run only through its CI/CD gate.
554
554
  Publish the ontology SDK before the agent SDK. Upgrade the workspace ontology before its agent,
555
555
  with the declared Telegram provider mapping and explicit `agent_chat:post_message` grant.
@@ -4,7 +4,7 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- var version = "0.13.0";
7
+ var version = "0.14.0";
8
8
  function agentBuild(environment = process.env) {
9
9
  return {
10
10
  agent: version,
@@ -0,0 +1,126 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ const __filename = __eveFileURLToPath(import.meta.url);
5
+ __eveDirname(__filename);
6
+ __eveCreateRequire(import.meta.url);
7
+ import { n as currentInboundRequest } from "./inbound-request-BXhiIph-.mjs";
8
+ import { randomUUID } from "node:crypto";
9
+ function telegramRuntimeSource(message, files, callbackQueryId) {
10
+ const attachments = [];
11
+ for (const file of message.attachments) {
12
+ const staged = files?.get(file.fileId);
13
+ if (staged?.status !== "stored" || attachments.length >= 10) continue;
14
+ let kind = file.kind;
15
+ for (const candidate of [
16
+ "voice",
17
+ "audio",
18
+ "video",
19
+ "video_note",
20
+ "sticker",
21
+ "animation"
22
+ ]) {
23
+ const raw = message.raw[candidate];
24
+ if (raw !== null && typeof raw === "object" && !Array.isArray(raw) && raw.file_id === file.fileId) kind = candidate;
25
+ }
26
+ attachments.push({
27
+ file_id: file.fileId,
28
+ kind,
29
+ staged_id: staged.attachment.id,
30
+ ...file.fileUniqueId === void 0 ? {} : { file_unique_id: file.fileUniqueId },
31
+ media_type: staged.attachment.contentType
32
+ });
33
+ }
34
+ return {
35
+ provider: "telegram",
36
+ chat_id: message.chat.id,
37
+ thread_id: String(message.messageThreadId ?? ""),
38
+ message_id: message.messageId,
39
+ ...message.replyToMessage === void 0 ? {} : { reply_to_message_id: message.replyToMessage.messageId },
40
+ ...message.from === void 0 ? {} : { sender: message.from.id },
41
+ ...callbackQueryId === void 0 ? {} : { callback_query_id: callbackQueryId },
42
+ ...attachments.length === 0 ? {} : { attachments }
43
+ };
44
+ }
45
+ function principalRuntimeSource(principal) {
46
+ if (principal?.authenticator !== "segmentstream-telegram") return;
47
+ const value = principal.attributes.segmentstreamRuntimeSource;
48
+ if (typeof value !== "string") return;
49
+ try {
50
+ return JSON.parse(value);
51
+ } catch {
52
+ return;
53
+ }
54
+ }
55
+ function runtimeEnvelope(input = {}) {
56
+ const source = input.source ?? principalRuntimeSource(input.principal);
57
+ return {
58
+ version: 1,
59
+ session_id: input.sessionId ?? randomUUID(),
60
+ call_id: input.callId ?? randomUUID(),
61
+ ...source === void 0 ? {} : { source }
62
+ };
63
+ }
64
+ function compatibleRuntimeEnvelope(envelope, extended) {
65
+ const source = envelope.source;
66
+ return extended || source === void 0 ? envelope : {
67
+ ...envelope,
68
+ source: {
69
+ provider: source.provider,
70
+ chat_id: source.chat_id,
71
+ thread_id: source.thread_id
72
+ }
73
+ };
74
+ }
75
+ const RUNTIME_SOURCE_PROTOCOL = "segmentstream/runtime-source";
76
+ const TELEGRAM_AUTHENTICATOR = "segmentstream-telegram";
77
+ function isGroup(message) {
78
+ return message.chat.type === "group" || message.chat.type === "supergroup";
79
+ }
80
+ function telegramPrincipal(input) {
81
+ const { message, member } = input;
82
+ const sender = message.from.id;
83
+ const attributes = {
84
+ memberId: member.id,
85
+ segmentstreamRuntimeSource: JSON.stringify(telegramRuntimeSource(message, currentInboundRequest()?.files, input.callbackQueryId)),
86
+ identityProvider: "telegram",
87
+ roles: member.roles,
88
+ chatId: message.chat.id,
89
+ chatType: message.chat.type
90
+ };
91
+ if (member.name !== null) attributes.memberName = member.name;
92
+ if (message.messageThreadId !== void 0) attributes.messageThreadId = String(message.messageThreadId);
93
+ if (message.from.username !== void 0) attributes.username = message.from.username;
94
+ const locale = member.locale ?? message.from.languageCode;
95
+ if (locale !== void 0) attributes.locale = locale;
96
+ const language = input.conversationLanguage ?? conversationLanguage([message.text || message.caption]);
97
+ if (language !== void 0) attributes.conversationLanguage = language;
98
+ return {
99
+ authenticator: TELEGRAM_AUTHENTICATOR,
100
+ issuer: "telegram",
101
+ principalId: isGroup(message) ? `telegram:${message.chat.id}:${sender}` : `telegram:${sender}`,
102
+ principalType: "user",
103
+ subject: sender,
104
+ attributes
105
+ };
106
+ }
107
+ function conversationLanguage(texts) {
108
+ for (const text of texts) {
109
+ if (/[\u0430-\u044f\u0451]/iu.test(text)) return "ru";
110
+ if ((text.match(/\b[a-z]+\b/gi)?.length ?? 0) >= 3) return "en";
111
+ }
112
+ }
113
+ function memberIdOf(principal) {
114
+ const memberId = principal?.attributes.memberId;
115
+ return typeof memberId === "string" && memberId !== "" ? memberId : void 0;
116
+ }
117
+ function rolesOf(principal) {
118
+ const roles = principal?.attributes.roles;
119
+ if (typeof roles === "string") return [roles];
120
+ return roles ?? [];
121
+ }
122
+ function identityProviderOf(principal) {
123
+ const provider = principal?.attributes.identityProvider;
124
+ return typeof provider === "string" && provider !== "" ? provider : void 0;
125
+ }
126
+ export { rolesOf as a, compatibleRuntimeEnvelope as c, telegramRuntimeSource as d, memberIdOf as i, principalRuntimeSource as l, conversationLanguage as n, telegramPrincipal as o, identityProviderOf as r, RUNTIME_SOURCE_PROTOCOL as s, TELEGRAM_AUTHENTICATOR as t, runtimeEnvelope as u };
@@ -1,5 +1,6 @@
1
1
  import { type GroupPolicyInput } from "@segmentstream-cloud/ontology/groups";
2
2
  import { type GroupPolicy } from "./lib/group-routing/policy.js";
3
+ import type { WorkspaceActionHook } from "./lib/telegram/workspace-action.js";
3
4
  import { type TelegramTexts } from "./lib/telegram/texts.js";
4
5
  import type { AttachmentLifecycle } from "./lib/telegram/lifecycle.js";
5
6
  import { type TelegramLinkPreviews } from "@segmentstream-cloud/ontology/telegram";
@@ -75,6 +76,9 @@ export interface SegmentStreamAgentConfigInput {
75
76
  readonly linkPreviews?: string;
76
77
  /** Additional exact HTTP(S) origins belonging to this workspace. */
77
78
  readonly additionalOwnOrigins?: readonly string[];
79
+ readonly callbacks?: Omit<WorkspaceActionHook, "contextVersion"> & {
80
+ readonly contextVersion: number;
81
+ };
78
82
  readonly menuButton?: {
79
83
  readonly text: LocalizedText;
80
84
  };
@@ -134,6 +138,7 @@ export interface TelegramSettings {
134
138
  readonly topicScope?: boolean;
135
139
  readonly commandsAddress?: boolean;
136
140
  };
141
+ readonly callbacks?: WorkspaceActionHook;
137
142
  readonly connection: string;
138
143
  readonly policy: TelegramPolicy;
139
144
  readonly linkPreviews?: TelegramLinkPreviews;
@@ -79,7 +79,8 @@ function telegramSettings(value, issues) {
79
79
  "linkPreviews",
80
80
  "additionalOwnOrigins",
81
81
  "attachmentLifecycle",
82
- "menuButton"
82
+ "menuButton",
83
+ "callbacks"
83
84
  ],
84
85
  issues
85
86
  });
@@ -122,6 +123,7 @@ function telegramSettings(value, issues) {
122
123
  message: "must be an array of HTTP(S) origins without paths, credentials, queries or fragments",
123
124
  path: [...path, "additionalOwnOrigins"]
124
125
  });
126
+ const callbacks = workspaceHook(telegram.callbacks, [...path, "callbacks"], issues);
125
127
  const menuButton = section({
126
128
  value: telegram.menuButton,
127
129
  path: [...path, "menuButton"],
@@ -159,6 +161,7 @@ function telegramSettings(value, issues) {
159
161
  connection,
160
162
  policy,
161
163
  ...signalSettings === void 0 ? {} : { groups: signalSettings },
164
+ ...callbacks === void 0 ? {} : { callbacks },
162
165
  linkPreviews,
163
166
  additionalOwnOrigins,
164
167
  ...menuText === void 0 ? {} : { menuButton: { text: menuText } },
@@ -169,6 +172,32 @@ function telegramSettings(value, issues) {
169
172
  } }
170
173
  } : null;
171
174
  }
175
+ function workspaceHook(value, path, issues) {
176
+ const configured = section({
177
+ value,
178
+ path,
179
+ allowed: [
180
+ "entity",
181
+ "action",
182
+ "contextVersion"
183
+ ],
184
+ issues
185
+ });
186
+ if (configured === void 0) return;
187
+ for (const key of ["entity", "action"]) if (typeof configured[key] !== "string" || !/^(?:[a-z][a-zA-Z0-9]*|[a-z][a-z0-9]*(?:_[a-z0-9]+)*)$/.test(String(configured[key]))) issues.push({
188
+ message: "must name an ontology entity or action",
189
+ path: [...path, key]
190
+ });
191
+ if (configured.contextVersion !== 2) issues.push({
192
+ message: "must be 2",
193
+ path: [...path, "contextVersion"]
194
+ });
195
+ return {
196
+ entity: String(configured.entity),
197
+ action: String(configured.action),
198
+ contextVersion: 2
199
+ };
200
+ }
172
201
  function modelId(input) {
173
202
  if (input.value === void 0) return input.fallback;
174
203
  if (typeof input.value !== "string" || input.value === "" || input.value !== input.value.trim()) {
@@ -5,9 +5,9 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { localizedText } from "../lib/admission.mjs";
8
- import "../lib/telegram/principal.mjs";
9
- import segmentstream from "../extension.mjs";
8
+ import "../../_chunks/principal-VFQWqe5S.mjs";
10
9
  import { confirmationLocale } from "../lib/telegram/confirmation-text.mjs";
10
+ import segmentstream from "../extension.mjs";
11
11
  import { defineHook } from "eve/hooks";
12
12
  var authorization_default = defineHook({ events: { "authorization.required": (event, context) => {
13
13
  const principal = context.session.auth.current;
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
- import { memberIdOf } from "./telegram/principal.mjs";
8
+ import { i as memberIdOf } from "../../_chunks/principal-VFQWqe5S.mjs";
9
9
  import { runtimeToken } from "./runtime-token.mjs";
10
10
  import { agentAttachments } from "./attachments.mjs";
11
11
  import { telegramConversation } from "./telegram/files.mjs";
@@ -1,3 +1,4 @@
1
+ import type { RuntimeEnvelope } from "./runtime-envelope.js";
1
2
  import type { RuntimeToken } from "./runtime-token.js";
2
3
  /**
3
4
  * The ontology service's attachment routes (`docs/agent.md`, "Attachments"). A file a member sends
@@ -35,6 +36,7 @@ export interface Attachment {
35
36
  readonly promotion: unknown;
36
37
  }
37
38
  export interface PromotionRequest {
39
+ readonly runtime?: RuntimeEnvelope;
38
40
  readonly entity: string;
39
41
  readonly action: string;
40
42
  readonly input?: Readonly<Record<string, unknown>>;
@@ -192,6 +192,7 @@ function agentAttachments(input) {
192
192
  const answer = await call(owner, `/${encodeURIComponent(id)}/promote`, "promotion", {
193
193
  entity: request.entity,
194
194
  action: request.action,
195
+ ...request.runtime === void 0 ? {} : { _meta: { _segmentstream: request.runtime } },
195
196
  input: request.input ?? {},
196
197
  ...request.field === void 0 ? {} : { field: request.field },
197
198
  ...request.recordId === void 0 ? {} : { record_id: request.recordId }
@@ -4,5 +4,5 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- import { t as agentBuild } from "../../_chunks/build-metadata-BLeBanNg.mjs";
7
+ import { t as agentBuild } from "../../_chunks/build-metadata-BB2uSGsI.mjs";
8
8
  export { agentBuild };
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { withTelegramTexts } from "./telegram/texts.mjs";
8
- import { memberIdOf } from "./telegram/principal.mjs";
8
+ import { i as memberIdOf } from "../../_chunks/principal-VFQWqe5S.mjs";
9
9
  import { confirmationCopy, confirmationLocale, renderConfirmation } from "./telegram/confirmation-text.mjs";
10
10
  import { chatPostCard } from "./telegram/chat-post-card.mjs";
11
11
  import { chatPostDigest, isChatPost } from "./chat-post-confirmation.mjs";
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { ENDS_CONVERSATION_INSTRUCTIONS } from "./group-routing/conversation-prompt.mjs";
8
- import { memberIdOf } from "./telegram/principal.mjs";
8
+ import { i as memberIdOf } from "../../_chunks/principal-VFQWqe5S.mjs";
9
9
  import { n as contentText, t as CONTENT_CAPS } from "../../_chunks/content-CvFsrmSD.mjs";
10
10
  import { conversationEvent, conversations, summarizeArguments } from "./conversations.mjs";
11
11
  import { parseAddress } from "./telegram/sessions.mjs";
@@ -1,3 +1,4 @@
1
+ import { type RuntimeEnvelope } from "./runtime-envelope.js";
1
2
  import type { ToolContext } from "eve/tools";
2
3
  import { type Confirmation } from "./confirmations.js";
3
4
  import { type Environment } from "./environment.js";
@@ -11,6 +12,7 @@ export interface DeferredConfirmation {
11
12
  readonly expiresAt: number;
12
13
  readonly status: "pending" | "approved" | "cancelled" | "expired" | "done" | "failed";
13
14
  readonly data: {
15
+ readonly runtimeSource?: RuntimeEnvelope["source"];
14
16
  readonly action: string;
15
17
  readonly input: Readonly<Record<string, unknown>>;
16
18
  readonly confirmation: Confirmation;
@@ -5,10 +5,10 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
- import { memberIdOf } from "./telegram/principal.mjs";
9
- import { runtimeToken } from "./runtime-token.mjs";
8
+ import { i as memberIdOf, l as principalRuntimeSource, u as runtimeEnvelope } from "../../_chunks/principal-VFQWqe5S.mjs";
10
9
  import { redactDiagnostic } from "./diagnostics.mjs";
11
10
  import { humanText, object } from "./confirmation-description.mjs";
11
+ import { runtimeToken } from "./runtime-token.mjs";
12
12
  import { chatPostDigest, chatPostOperationId, isChatPost } from "./chat-post-confirmation.mjs";
13
13
  import { actionApproval, inputHash } from "./confirmations.mjs";
14
14
  import { ontologyTools } from "./ontology-tools.mjs";
@@ -37,7 +37,7 @@ function deferredConfirmations(options = {}) {
37
37
  }
38
38
  async function executeDeferredTool(memberId, action, input, ctx, options = {}) {
39
39
  if (memberIdOf(ctx.session.auth.current) !== memberId) throw new Error("The ontology tool belongs to another member.");
40
- if (Object.hasOwn(input, "_segmentstream")) throw new Error("Runtime call metadata is application-owned.");
40
+ input = Object.fromEntries(Object.entries(input).filter(([key]) => key !== "_segmentstream"));
41
41
  const principal = ctx.session.auth.current;
42
42
  const attributes = principal?.attributes;
43
43
  const id = chatPostOperationId(ctx.session.id, ctx.callId);
@@ -56,7 +56,15 @@ async function executeDeferredTool(memberId, action, input, ctx, options = {}) {
56
56
  };
57
57
  };
58
58
  if (existing !== null) return boundResult(existing);
59
- const tools = ontologyTools(options);
59
+ const tools = ontologyTools({
60
+ ...options,
61
+ principal: ctx.session.auth.current,
62
+ envelope: runtimeEnvelope({
63
+ principal: ctx.session.auth.current,
64
+ sessionId: ctx.session.id,
65
+ callId: ctx.callId
66
+ })
67
+ });
60
68
  let saved = {};
61
69
  const result = await actionApproval({
62
70
  ...tools,
@@ -96,6 +104,7 @@ async function executeDeferredTool(memberId, action, input, ctx, options = {}) {
96
104
  data: {
97
105
  action,
98
106
  input,
107
+ ...principalRuntimeSource(principal) === void 0 ? {} : { runtimeSource: principalRuntimeSource(principal) },
99
108
  confirmation,
100
109
  sessionId: ctx.session.id,
101
110
  callId: ctx.callId,
@@ -125,11 +134,13 @@ async function runDeferredAction(record, options) {
125
134
  session_id: record.data.sessionId,
126
135
  call_id: record.data.callId,
127
136
  operation_id: record.id,
137
+ ...record.data.runtimeSource === void 0 ? {} : { source: record.data.runtimeSource },
128
138
  ...preview ? {
129
139
  source: {
130
140
  provider: "telegram",
131
141
  chat_id: record.data.chatId,
132
- thread_id: record.data.threadId
142
+ thread_id: record.data.threadId,
143
+ ...record.data.runtimeSource
133
144
  },
134
145
  expected: {
135
146
  chat_id: preview.chatId,
@@ -137,12 +148,12 @@ async function runDeferredAction(record, options) {
137
148
  }
138
149
  } : {}
139
150
  };
140
- const result = await tools.rpc(record.memberId, "tools/call", {
151
+ const result = await ontologyTools({
152
+ ...options,
153
+ envelope
154
+ }).rpc(record.memberId, "tools/call", {
141
155
  name: record.data.action.split("__").at(-1),
142
- arguments: {
143
- ...record.data.input,
144
- _segmentstream: envelope
145
- }
156
+ arguments: record.data.input
146
157
  });
147
158
  const output = result.structuredContent;
148
159
  if (result.isError === true || (output?.validation)?.ok === false || preview !== void 0 && output?.status !== "sent") throw new Error("The confirmed action did not complete; its outcome may be uncertain.", { cause: result });
@@ -36,6 +36,7 @@ export declare function ontologyConnection(input: {
36
36
  readonly fetch?: typeof fetch;
37
37
  /** Set only by the authenticated catalog handshake, never by model input. */
38
38
  readonly chatPosting?: boolean;
39
+ readonly runtimeSource?: boolean;
39
40
  }): McpClientConnectionDefinition | null;
40
41
  /** Negotiate before opening Eve's client: old strict schemas never receive the new envelope. */
41
42
  export declare function ontologyConnectionForTurn(input: Parameters<typeof ontologyConnection>[0]): Promise<McpClientConnectionDefinition | null>;
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
- import { identityProviderOf, memberIdOf } from "./telegram/principal.mjs";
8
+ import { c as compatibleRuntimeEnvelope, i as memberIdOf, r as identityProviderOf, u as runtimeEnvelope } from "../../_chunks/principal-VFQWqe5S.mjs";
9
9
  import { runtimeToken, tokenExpiry } from "./runtime-token.mjs";
10
10
  import { CHAT_POST_ENVELOPE, chatPostDigest, chatPostOperationId, isChatPost } from "./chat-post-confirmation.mjs";
11
11
  import { actionApproval, confirmations } from "./confirmations.mjs";
@@ -31,21 +31,23 @@ function ontologyConnection(input) {
31
31
  if (memberId === void 0) return null;
32
32
  const origin = appOrigin(input.environment ?? process.env);
33
33
  const token = input.token ?? runtimeToken;
34
+ const chatProtocol = input.chatPosting ? ":chat-post-v1" : "";
35
+ const sourceProtocol = input.runtimeSource ? ":runtime-source-v1" : "";
34
36
  return defineMcpClientConnection({
35
37
  url: `${origin}/mcp`,
36
38
  description: DESCRIPTION,
37
- instanceKey: `${origin}/mcp#member:${memberId}${input.chatPosting ? ":chat-post-v1" : ""}`,
39
+ instanceKey: `${origin}/mcp#member:${memberId}${chatProtocol}${sourceProtocol}`,
38
40
  approval: actionApproval(ontologyTools(input)),
39
- ...input.chatPosting ? { toolCall: { providedArguments: { [CHAT_POST_ENVELOPE]: (ctx) => {
41
+ ...input.chatPosting || input.runtimeSource ? { toolCall: { providedArguments: { [CHAT_POST_ENVELOPE]: (ctx) => {
40
42
  actAsHeaders({
41
43
  principal: ctx.session.auth.current,
42
44
  expectedMemberId: memberId
43
45
  });
44
- const base = {
45
- version: 1,
46
- session_id: ctx.session.id,
47
- call_id: ctx.callId
48
- };
46
+ const base = compatibleRuntimeEnvelope(runtimeEnvelope({
47
+ principal: ctx.session.auth.current,
48
+ sessionId: ctx.session.id,
49
+ callId: ctx.callId
50
+ }), input.runtimeSource === true);
49
51
  if (!isChatPost(ctx.toolName)) return base;
50
52
  const saved = confirmations.get()[ctx.callId];
51
53
  const preview = saved?.description?.chatPost;
@@ -56,7 +58,8 @@ function ontologyConnection(input) {
56
58
  source: {
57
59
  provider: "telegram",
58
60
  chat_id: saved.source.chatId,
59
- thread_id: saved.source.threadId
61
+ thread_id: saved.source.threadId,
62
+ ...base.source
60
63
  },
61
64
  expected: {
62
65
  chat_id: preview.chatId,
@@ -84,15 +87,18 @@ function ontologyConnection(input) {
84
87
  async function ontologyConnectionForTurn(input) {
85
88
  const memberId = memberIdOf(input.principal);
86
89
  if (memberId === void 0) return null;
87
- let chatPosting = false;
90
+ let capabilities = {
91
+ chatPosting: false,
92
+ runtimeSource: false
93
+ };
88
94
  try {
89
- chatPosting = await ontologyTools(input).supportsChatPosting(memberId);
95
+ capabilities = await ontologyTools(input).capabilities(memberId);
90
96
  } catch (error) {
91
97
  console.warn("Chat posting capability unavailable; keeping the compatible ontology connection.", error);
92
98
  }
93
99
  return ontologyConnection({
94
100
  ...input,
95
- chatPosting
101
+ ...capabilities
96
102
  });
97
103
  }
98
104
  export { ACT_AS_HEADER, MemberRequiredError, actAsHeaders, ontologyConnection, ontologyConnectionForTurn };
@@ -4,7 +4,7 @@ import { createRequire as __eveCreateRequire } from "node:module";
4
4
  const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
- import { memberIdOf } from "./telegram/principal.mjs";
7
+ import { i as memberIdOf, u as runtimeEnvelope } from "../../_chunks/principal-VFQWqe5S.mjs";
8
8
  import { object } from "./confirmation-description.mjs";
9
9
  import { readOntologyMetadata } from "./ontology-metadata-read.mjs";
10
10
  async function ontologyMetadata(memberId, operation, input, context, options = {}) {
@@ -16,7 +16,14 @@ async function ontologyMetadata(memberId, operation, input, context, options = {
16
16
  name: operation,
17
17
  arguments: input
18
18
  }
19
- }, context, options);
19
+ }, context, {
20
+ ...options,
21
+ envelope: runtimeEnvelope({
22
+ principal,
23
+ sessionId: context.session.id,
24
+ callId: context.callId
25
+ })
26
+ });
20
27
  return result.structuredContent === void 0 ? result : object(result.structuredContent);
21
28
  }
22
29
  async function ontologyInspect(memberId, input, context, options = {}) {
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
- import { memberIdOf } from "./telegram/principal.mjs";
8
+ import { i as memberIdOf } from "../../_chunks/principal-VFQWqe5S.mjs";
9
9
  import { redactDiagnostic } from "./diagnostics.mjs";
10
10
  import { object } from "./confirmation-description.mjs";
11
11
  import { OntologyToolRejected, ontologyTools } from "./ontology-tools.mjs";
@@ -1,3 +1,4 @@
1
+ import { type RuntimeEnvelope } from "./runtime-envelope.js";
1
2
  import type { SessionAuthContext } from "eve/context";
2
3
  import type { ActionAnnotations } from "./confirmations.js";
3
4
  import { type Environment } from "./environment.js";
@@ -13,17 +14,23 @@ export declare class OntologyActionNotPermitted extends Error {
13
14
  /** A fresh, permission-filtered catalog. Listing tools cannot run an action's prepare step. */
14
15
  export declare function ontologyTools(options?: {
15
16
  readonly principal?: SessionAuthContext | null;
17
+ readonly envelope?: RuntimeEnvelope;
16
18
  readonly environment?: Environment;
17
19
  readonly token?: RuntimeToken;
18
20
  readonly fetch?: typeof fetch;
19
21
  }): {
20
22
  rpc: (memberId: string, method: string, params: Record<string, unknown>, signal?: AbortSignal) => Promise<Record<string, unknown>>;
21
23
  list(memberId: string): Promise<Record<string, unknown>[]>;
24
+ supportsRuntimeSource(memberId: string, signal?: AbortSignal): Promise<boolean>;
25
+ capabilities(memberId: string): Promise<{
26
+ chatPosting: boolean;
27
+ runtimeSource: boolean;
28
+ }>;
22
29
  supportsChatPosting(memberId: string): Promise<boolean>;
23
- labels(memberId: string, action: string, input?: Readonly<Record<string, unknown>>): Promise<ActionAnnotations>;
30
+ labels(memberId: string, action: string, input?: Readonly<Record<string, unknown>>, signal?: AbortSignal): Promise<ActionAnnotations>;
24
31
  describe(memberId: string, action: string, input: Readonly<Record<string, unknown>>): Promise<import("./confirmation-description.js").ConfirmationDescription>;
25
32
  };
26
- export declare function ontologyToolLabels(options?: Parameters<typeof ontologyTools>[0]): (memberId: string, action: string, input?: Readonly<Record<string, unknown>>) => Promise<ActionAnnotations>;
33
+ export declare function ontologyToolLabels(options?: Parameters<typeof ontologyTools>[0]): (memberId: string, action: string, input?: Readonly<Record<string, unknown>>, signal?: AbortSignal) => Promise<ActionAnnotations>;
27
34
  /** MCP normalizes an authored camelCase action to snake_case. */
28
35
  export declare function actionToolName(entity: string, action: string): string;
29
36
  /** One target contract for native and MCP attachment promotion; never trust supplied safety hints. */
@@ -5,10 +5,10 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { appOrigin } from "./environment.mjs";
8
- import { identityProviderOf, memberIdOf } from "./telegram/principal.mjs";
9
- import { runtimeToken } from "./runtime-token.mjs";
8
+ import { c as compatibleRuntimeEnvelope, i as memberIdOf, r as identityProviderOf, s as RUNTIME_SOURCE_PROTOCOL, u as runtimeEnvelope } from "../../_chunks/principal-VFQWqe5S.mjs";
10
9
  import { redactDiagnostic } from "./diagnostics.mjs";
11
10
  import { describeConfirmation, object } from "./confirmation-description.mjs";
11
+ import { runtimeToken } from "./runtime-token.mjs";
12
12
  import { describeChatPost, isChatPost, supportsChatPost } from "./chat-post-confirmation.mjs";
13
13
  import { randomUUID } from "node:crypto";
14
14
  var OntologyToolRejected = class extends Error {
@@ -18,7 +18,19 @@ var OntologyActionNotPermitted = class extends Error {
18
18
  code = "ontology_action_not_permitted";
19
19
  };
20
20
  function ontologyTools(options = {}) {
21
+ const runtimeSources = new Map();
21
22
  async function rpc(memberId, method, params, signal) {
23
+ if (method === "tools/call") {
24
+ const envelope = options.envelope ?? runtimeEnvelope({ principal: memberIdOf(options.principal) === memberId ? options.principal : null });
25
+ if (envelope.source !== void 0 && !runtimeSources.has(memberId)) await rpc(memberId, "tools/list", {}, signal);
26
+ params = {
27
+ ...params,
28
+ arguments: {
29
+ ...object(params.arguments),
30
+ _segmentstream: compatibleRuntimeEnvelope(envelope, runtimeSources.get(memberId) === true)
31
+ }
32
+ };
33
+ }
22
34
  const provider = memberIdOf(options.principal) === memberId ? identityProviderOf(options.principal) : void 0;
23
35
  const credential = await (options.token ?? runtimeToken)();
24
36
  const secrets = new Set([credential]);
@@ -46,6 +58,7 @@ function ontologyTools(options = {}) {
46
58
  const body = object(await response.json());
47
59
  if (body.error !== void 0) throw new Error("Ontology metadata unavailable.", { cause: body.error });
48
60
  const result = object(body.result);
61
+ if (method === "tools/list" && Array.isArray(result.tools)) runtimeSources.set(memberId, result.tools.map(object).some((tool) => object(tool._meta)[RUNTIME_SOURCE_PROTOCOL] === 1));
49
62
  if (result.isError === true) {
50
63
  const message = (Array.isArray(result.content) ? result.content : []).map(object).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n");
51
64
  if ([
@@ -119,12 +132,23 @@ function ontologyTools(options = {}) {
119
132
  const result = await rpc(memberId, "tools/list", {});
120
133
  return Array.isArray(result.tools) ? result.tools.map(object) : [];
121
134
  },
135
+ async supportsRuntimeSource(memberId, signal) {
136
+ if (!runtimeSources.has(memberId)) await rpc(memberId, "tools/list", {}, signal);
137
+ return runtimeSources.get(memberId) === true;
138
+ },
139
+ async capabilities(memberId) {
140
+ const result = await rpc(memberId, "tools/list", {});
141
+ return {
142
+ chatPosting: Array.isArray(result.tools) && result.tools.map(object).some(supportsChatPost),
143
+ runtimeSource: runtimeSources.get(memberId) === true
144
+ };
145
+ },
122
146
  async supportsChatPosting(memberId) {
123
147
  const result = await rpc(memberId, "tools/list", {});
124
148
  return Array.isArray(result.tools) && result.tools.map(object).some(supportsChatPost);
125
149
  },
126
- async labels(memberId, action, input = {}) {
127
- const { tool } = await resolve(memberId, action, input);
150
+ async labels(memberId, action, input = {}, signal) {
151
+ const { tool } = await resolve(memberId, action, input, signal);
128
152
  return {
129
153
  ...object(tool.annotations),
130
154
  confirmation: object(object(tool.inputSchema).properties)._confirmation !== void 0
@@ -0,0 +1,28 @@
1
+ import type { RuntimeSource } from "@segmentstream-cloud/ontology";
2
+ import type { SessionAuthContext } from "eve/context";
3
+ import type { TelegramMessage } from "eve/channels/telegram";
4
+ import type { StagedFile } from "./telegram/inbound-request.js";
5
+ export type RuntimeEnvelope = {
6
+ readonly version: 1;
7
+ readonly session_id: string;
8
+ readonly call_id: string;
9
+ readonly operation_id?: string;
10
+ readonly source?: RuntimeSource;
11
+ readonly expected?: {
12
+ readonly chat_id: string;
13
+ readonly digest: string;
14
+ };
15
+ };
16
+ /** The source comes from the verified Telegram message and admitted storage results. */
17
+ export declare function telegramRuntimeSource(message: Pick<TelegramMessage, "chat" | "messageId" | "messageThreadId" | "replyToMessage" | "from" | "raw" | "attachments">, files?: ReadonlyMap<string, StagedFile>, callbackQueryId?: string): RuntimeSource;
18
+ /** Session auth is SDK-owned; no tool argument is read while rebuilding provenance. */
19
+ export declare function principalRuntimeSource(principal?: SessionAuthContext | null): RuntimeSource | undefined;
20
+ export declare function runtimeEnvelope(input?: {
21
+ readonly principal?: SessionAuthContext | null;
22
+ readonly sessionId?: string;
23
+ readonly callId?: string;
24
+ readonly source?: RuntimeSource;
25
+ }): RuntimeEnvelope;
26
+ /** Older strict ontology schemas accept only the original chat identity fields. */
27
+ export declare function compatibleRuntimeEnvelope(envelope: RuntimeEnvelope, extended: boolean): RuntimeEnvelope;
28
+ export declare const RUNTIME_SOURCE_PROTOCOL = "segmentstream/runtime-source";
@@ -0,0 +1,8 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ const __filename = __eveFileURLToPath(import.meta.url);
5
+ __eveDirname(__filename);
6
+ __eveCreateRequire(import.meta.url);
7
+ import { c as compatibleRuntimeEnvelope, d as telegramRuntimeSource, l as principalRuntimeSource, s as RUNTIME_SOURCE_PROTOCOL, u as runtimeEnvelope } from "../../_chunks/principal-VFQWqe5S.mjs";
8
+ export { RUNTIME_SOURCE_PROTOCOL, compatibleRuntimeEnvelope, principalRuntimeSource, runtimeEnvelope, telegramRuntimeSource };