@segmentstream/agent 0.20.3 → 0.21.1

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 (67) hide show
  1. package/README.md +47 -4
  2. package/dist/_chunks/{build-metadata-Bgh-kwt7.mjs → build-metadata-kBu1knj6.mjs} +1 -1
  3. package/dist/_chunks/{principal-DFcIfKSa.mjs → principal-69qoDdqy.mjs} +2 -0
  4. package/dist/extension/channels/tasks.d.ts +7 -0
  5. package/dist/extension/channels/tasks.mjs +9 -0
  6. package/dist/extension/connections/ontology.d.ts +1 -1
  7. package/dist/extension/hooks/authorization.mjs +1 -1
  8. package/dist/extension/instructions/conversations.d.ts +4 -1
  9. package/dist/extension/instructions/conversations.mjs +2 -1
  10. package/dist/extension/lib/actor.d.ts +39 -0
  11. package/dist/extension/lib/actor.mjs +48 -0
  12. package/dist/extension/lib/agent-task-channel.d.ts +45 -0
  13. package/dist/extension/lib/agent-task-channel.mjs +201 -0
  14. package/dist/extension/lib/agent-task.d.ts +127 -0
  15. package/dist/extension/lib/agent-task.mjs +183 -0
  16. package/dist/extension/lib/attachment-tools.mjs +1 -1
  17. package/dist/extension/lib/attachments.mjs +1 -1
  18. package/dist/extension/lib/build-metadata.mjs +1 -1
  19. package/dist/extension/lib/chat-post-confirmation.d.ts +2 -0
  20. package/dist/extension/lib/chat-post-confirmation.mjs +3 -2
  21. package/dist/extension/lib/confirmation-outcome.d.ts +1 -1
  22. package/dist/extension/lib/confirmations.d.ts +8 -3
  23. package/dist/extension/lib/confirmations.mjs +17 -5
  24. package/dist/extension/lib/conversation-runtime.mjs +12 -2
  25. package/dist/extension/lib/deferred-confirmations.mjs +5 -4
  26. package/dist/extension/lib/group-routing/dequeue.mjs +1 -1
  27. package/dist/extension/lib/ontology-connection.d.ts +18 -16
  28. package/dist/extension/lib/ontology-connection.mjs +47 -42
  29. package/dist/extension/lib/ontology-file-call.mjs +26 -21
  30. package/dist/extension/lib/ontology-tools.d.ts +12 -9
  31. package/dist/extension/lib/ontology-tools.mjs +42 -33
  32. package/dist/extension/lib/runtime-envelope.mjs +1 -1
  33. package/dist/extension/lib/sandbox-session.mjs +1 -1
  34. package/dist/extension/lib/stored-files.mjs +3 -2
  35. package/dist/extension/lib/telegram/adapter.mjs +5 -5
  36. package/dist/extension/lib/telegram/attachments-send.mjs +4 -4
  37. package/dist/extension/lib/telegram/callbacks.mjs +1 -1
  38. package/dist/extension/lib/telegram/confirmation-expiry.mjs +2 -2
  39. package/dist/extension/lib/telegram/confirmations.mjs +2 -2
  40. package/dist/extension/lib/telegram/deferred-confirmations.mjs +1 -1
  41. package/dist/extension/lib/telegram/durable-typing.mjs +1 -1
  42. package/dist/extension/lib/telegram/files.mjs +1 -1
  43. package/dist/extension/lib/telegram/health.mjs +1 -1
  44. package/dist/extension/lib/telegram/held-approvals.d.ts +1 -1
  45. package/dist/extension/lib/telegram/inbound.mjs +1 -1
  46. package/dist/extension/lib/telegram/lifecycle.mjs +6 -5
  47. package/dist/extension/lib/telegram/principal.d.ts +4 -1
  48. package/dist/extension/lib/telegram/principal.mjs +1 -1
  49. package/dist/extension/lib/telegram/silent.mjs +1 -1
  50. package/dist/extension/lib/telegram/structured.mjs +1 -1
  51. package/dist/extension/lib/telegram/turn-errors.mjs +1 -1
  52. package/dist/extension/lib/telegram/workspace-action.mjs +4 -3
  53. package/dist/extension/model.mjs +2 -2
  54. package/dist/extension/tools/attachments_discard.d.ts +1 -7
  55. package/dist/extension/tools/attachments_discard.mjs +3 -1
  56. package/dist/extension/tools/attachments_list.d.ts +1 -7
  57. package/dist/extension/tools/attachments_list.mjs +3 -1
  58. package/dist/extension/tools/attachments_promote.d.ts +1 -4
  59. package/dist/extension/tools/attachments_promote.mjs +8 -6
  60. package/dist/extension/tools/attachments_read.d.ts +1 -8
  61. package/dist/extension/tools/attachments_read.mjs +3 -1
  62. package/dist/extension/tools/attachments_send.d.ts +1 -1
  63. package/dist/extension/tools/attachments_send.mjs +3 -1
  64. package/dist/extension/tools/attachments_table.d.ts +1 -3
  65. package/dist/extension/tools/attachments_table.mjs +3 -1
  66. package/dist/extension/tools/questions_ask.mjs +14 -1
  67. package/package.json +2 -2
@@ -0,0 +1,183 @@
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 { redactDiagnostic } from "./diagnostics.mjs";
8
+ import { appOrigin } from "./environment.mjs";
9
+ import { runtimeToken } from "./runtime-token.mjs";
10
+ import { randomUUID } from "node:crypto";
11
+ import { defineState } from "eve/context";
12
+ const AGENT_TASK_AUTHENTICATOR = "segmentstream-agent-task";
13
+ const AGENT_TASK_HEADER = "X-SegmentStream-Agent-Task";
14
+ const AGENT_TASK_ROUTE = "/eve/segmentstream/v1/tasks";
15
+ const AGENT_TASK_END_ROUTE = "/eve/segmentstream/v1/tasks/end";
16
+ const MAXIMUM_TASK_TEXT = 65536;
17
+ const CALL_TIMEOUT_MS = 15e3;
18
+ const FIRST_RETRY_MS = 1e3;
19
+ const MAXIMUM_RETRY_MS = 3e4;
20
+ const REPORT_WINDOW_MS = 12e4;
21
+ function taskAddress(taskId) {
22
+ return `task:${taskId}`;
23
+ }
24
+ function taskPrincipal(task) {
25
+ return {
26
+ authenticator: AGENT_TASK_AUTHENTICATOR,
27
+ principalType: "service",
28
+ principalId: task.principal.id,
29
+ attributes: {
30
+ taskId: task.id,
31
+ workflow: task.workflow,
32
+ roles: task.principal.roles,
33
+ name: task.principal.name
34
+ }
35
+ };
36
+ }
37
+ function agentTaskIdOf(principal) {
38
+ if (principal?.authenticator !== "segmentstream-agent-task") return;
39
+ const taskId = principal.attributes.taskId;
40
+ return typeof taskId === "string" && taskId !== "" ? taskId : void 0;
41
+ }
42
+ const agentTaskSessions = defineState("segmentstream.agent-task", () => null);
43
+ function taskSecret(taskId, state = agentTaskSessions) {
44
+ const session = state.get();
45
+ return session?.taskId === taskId && session.secret !== null ? session.secret : void 0;
46
+ }
47
+ function resultBody(outcome) {
48
+ if (outcome.status === "failed") return {
49
+ status: "failed",
50
+ reason: outcome.reason
51
+ };
52
+ if (outcome.text.length <= 65536) return {
53
+ status: outcome.status,
54
+ text: outcome.text,
55
+ truncated: false
56
+ };
57
+ const last = outcome.text.charCodeAt(65535);
58
+ const end = last >= 55296 && last <= 56319 ? 65535 : MAXIMUM_TASK_TEXT;
59
+ return {
60
+ status: outcome.status,
61
+ text: outcome.text.slice(0, end),
62
+ truncated: true
63
+ };
64
+ }
65
+ var AgentTaskError = class extends Error {
66
+ status;
67
+ name = "AgentTaskError";
68
+ constructor(message, status, options) {
69
+ super(message, options);
70
+ this.status = status;
71
+ }
72
+ };
73
+ function isRecord(value) {
74
+ return typeof value === "object" && value !== null && !Array.isArray(value);
75
+ }
76
+ function currentTask(value) {
77
+ const principal = isRecord(value) ? value.principal : void 0;
78
+ if (!isRecord(value) || typeof value.id !== "string" || value.id === "" || typeof value.workflow !== "string" || typeof value.prompt !== "string" || typeof value.deadline_at !== "string" || Number.isNaN(Date.parse(value.deadline_at)) || !isRecord(principal) || typeof principal.id !== "string" || principal.id === "" || typeof principal.name !== "string" || !Array.isArray(principal.roles) || !principal.roles.every((role) => typeof role === "string")) throw new AgentTaskError("The ontology answered the current task with an invalid body.");
79
+ return {
80
+ id: value.id,
81
+ workflow: value.workflow,
82
+ prompt: value.prompt,
83
+ deadlineAt: value.deadline_at,
84
+ principal: {
85
+ id: principal.id,
86
+ name: principal.name,
87
+ roles: principal.roles
88
+ }
89
+ };
90
+ }
91
+ function transient(status) {
92
+ return status === 408 || status === 429 || status >= 500;
93
+ }
94
+ function agentTasks(options = {}) {
95
+ const environment = options.environment ?? process.env;
96
+ const token = options.token ?? runtimeToken;
97
+ const fetchImpl = options.fetch ?? fetch;
98
+ const now = options.now ?? Date.now;
99
+ const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
100
+ const log = options.log ?? ((message, detail) => console.error(message, detail));
101
+ async function call(path, init) {
102
+ const credential = await token();
103
+ try {
104
+ return await fetchImpl(`${appOrigin(environment)}${path}`, {
105
+ method: init.method,
106
+ redirect: "error",
107
+ signal: AbortSignal.timeout(CALL_TIMEOUT_MS),
108
+ headers: {
109
+ authorization: `Bearer ${credential}`,
110
+ accept: "application/json",
111
+ ...init.secret === void 0 ? {} : { [AGENT_TASK_HEADER]: init.secret },
112
+ ...init.body === void 0 ? {} : { "content-type": "application/json" }
113
+ },
114
+ ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) }
115
+ });
116
+ } catch (error) {
117
+ throw new AgentTaskError(`The task request to ${path} failed.`, void 0, { cause: redactDiagnostic(error, new Set([credential, init.secret ?? ""])) });
118
+ }
119
+ }
120
+ return {
121
+ async current(secret) {
122
+ const response = await call("/api/ontology/agent/tasks/current", {
123
+ method: "GET",
124
+ secret
125
+ });
126
+ if (response.status === 403) return { status: "refused" };
127
+ if (!response.ok) throw new AgentTaskError(`The ontology answered the current task with HTTP ${response.status}.`, response.status);
128
+ return {
129
+ status: "live",
130
+ task: currentTask(await response.json())
131
+ };
132
+ },
133
+ async report(secret, outcome, context) {
134
+ const reference = randomUUID();
135
+ const stop = Math.min(Date.parse(context.deadlineAt), now() + REPORT_WINDOW_MS);
136
+ const body = resultBody(outcome);
137
+ for (let delay = FIRST_RETRY_MS;; delay = Math.min(delay * 2, MAXIMUM_RETRY_MS)) {
138
+ let failure;
139
+ try {
140
+ const response = await call("/api/ontology/agent/tasks/current/result", {
141
+ method: "POST",
142
+ secret,
143
+ body
144
+ });
145
+ if (response.ok) return "accepted";
146
+ if (response.status === 409) return "already_reported";
147
+ if (response.status === 403) return "refused";
148
+ failure = new AgentTaskError(`The ontology answered the task report with HTTP ${response.status}.`, response.status);
149
+ if (!transient(response.status)) {
150
+ log("[segmentstream] agent_task_report_failed", {
151
+ reference,
152
+ taskId: context.taskId,
153
+ error: failure
154
+ });
155
+ return "failed";
156
+ }
157
+ } catch (error) {
158
+ failure = error;
159
+ }
160
+ if (now() + delay >= stop) {
161
+ log("[segmentstream] agent_task_report_failed", {
162
+ reference,
163
+ taskId: context.taskId,
164
+ error: failure
165
+ });
166
+ return "failed";
167
+ }
168
+ await sleep(delay);
169
+ }
170
+ },
171
+ async end(input) {
172
+ const response = await call(AGENT_TASK_END_ROUTE, {
173
+ method: "POST",
174
+ body: {
175
+ task_id: input.taskId,
176
+ session_id: input.sessionId
177
+ }
178
+ });
179
+ if (!response.ok) throw new AgentTaskError(`The agent answered the session end with HTTP ${response.status}.`, response.status);
180
+ }
181
+ };
182
+ }
183
+ export { AGENT_TASK_AUTHENTICATOR, AGENT_TASK_END_ROUTE, AGENT_TASK_HEADER, AGENT_TASK_ROUTE, AgentTaskError, MAXIMUM_TASK_TEXT, agentTaskIdOf, agentTaskSessions, agentTasks, resultBody, taskAddress, taskPrincipal, taskSecret };
@@ -5,8 +5,8 @@ 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 { i as memberIdOf } from "../../_chunks/principal-DFcIfKSa.mjs";
9
8
  import { runtimeToken } from "./runtime-token.mjs";
9
+ import { i as memberIdOf } from "../../_chunks/principal-69qoDdqy.mjs";
10
10
  import { agentAttachments } from "./attachments.mjs";
11
11
  import { telegramConversation } from "./telegram/files.mjs";
12
12
  import { detectFile, documentText } from "./attachment-formats.mjs";
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { MAXIMUM_DOWNLOAD_BYTES } from "./connection-proxy.mjs";
8
- import { ACT_AS_HEADER } from "./ontology-connection.mjs";
8
+ import { ACT_AS_HEADER } from "./actor.mjs";
9
9
  import { createHash } from "node:crypto";
10
10
  const MAXIMUM_ATTACHMENT_BYTES = MAXIMUM_DOWNLOAD_BYTES;
11
11
  const ATTACHMENT_TTL_HOURS = 24;
@@ -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-Bgh-kwt7.mjs";
7
+ import { t as agentBuild } from "../../_chunks/build-metadata-kBu1knj6.mjs";
8
8
  export { agentBuild };
@@ -2,6 +2,8 @@ import type { ConfirmationDescription } from "./confirmation-description.js";
2
2
  /** Versioned agreement with the authenticated ontology, not a model supplied capability. */
3
3
  export declare const CHAT_POST_PROTOCOL = "segmentstream/chat-post";
4
4
  export declare const CHAT_POST_ENVELOPE = "_segmentstream";
5
+ /** The ontology's chat posting tool, without the connection's namespace. */
6
+ export declare const CHAT_POST_TOOL = "chat_post_message";
5
7
  export interface ChatPostPreview {
6
8
  readonly chatId: string;
7
9
  readonly provider: "telegram";
@@ -9,9 +9,10 @@ import { createHash } from "node:crypto";
9
9
  import { TelegramFormatConverter, escapeMarkdownV2 } from "@chat-adapter/telegram";
10
10
  const CHAT_POST_PROTOCOL = "segmentstream/chat-post";
11
11
  const CHAT_POST_ENVELOPE = "_segmentstream";
12
+ const CHAT_POST_TOOL = "chat_post_message";
12
13
  const converter = new TelegramFormatConverter();
13
14
  function isChatPost(action) {
14
- return action.split("__").at(-1) === "chat_post_message";
15
+ return action.split("__").at(-1) === CHAT_POST_TOOL;
15
16
  }
16
17
  function supportsChatPost(tool) {
17
18
  return isChatPost(String(tool.name)) && object(tool._meta)["segmentstream/chat-post"] === 1;
@@ -64,4 +65,4 @@ function describeChatPost(input) {
64
65
  }
65
66
  };
66
67
  }
67
- export { CHAT_POST_ENVELOPE, CHAT_POST_PROTOCOL, chatPostDigest, chatPostOperationId, describeChatPost, isChatPost, supportsChatPost };
68
+ export { CHAT_POST_ENVELOPE, CHAT_POST_PROTOCOL, CHAT_POST_TOOL, chatPostDigest, chatPostOperationId, describeChatPost, isChatPost, supportsChatPost };
@@ -22,6 +22,6 @@ export declare function confirmationOutcome(record: DeferredConfirmation, status
22
22
  request?: string | undefined;
23
23
  } | undefined;
24
24
  confirmation: string;
25
- status: "expired" | "done" | "cancelled" | "failed" | "approved";
25
+ status: "failed" | "expired" | "done" | "cancelled" | "approved";
26
26
  summary: string;
27
27
  };
@@ -1,5 +1,6 @@
1
1
  import { type StateHandle } from "eve/context";
2
2
  import type { ApprovalConfiguration } from "eve/tools/approval";
3
+ import { type OntologyActor } from "./actor.js";
3
4
  import type { ConfirmationDescription } from "./confirmation-description.js";
4
5
  import { type ConfirmationCopy } from "./telegram/confirmation-text.js";
5
6
  import { type TelegramTexts } from "./telegram/texts.js";
@@ -30,10 +31,14 @@ export interface Confirmation {
30
31
  readonly cancellationReason?: "edit" | "expired";
31
32
  }
32
33
  export declare const confirmations: StateHandle<Record<string, Confirmation>>;
33
- /** Eve pauses the original call and rechecks this policy immediately before executing it. */
34
+ /**
35
+ * Eve pauses the original call and rechecks this policy immediately before executing it. In a task
36
+ * session no person is present: an action the workflow's role permits runs without a confirmation
37
+ * card, because the role is the boundary, and chat posting is refused.
38
+ */
34
39
  export declare function actionApproval(options: {
35
- readonly labels: (memberId: string, action: string, input: Readonly<Record<string, unknown>>) => Promise<ActionAnnotations>;
36
- readonly describe?: (memberId: string, action: string, input: Readonly<Record<string, unknown>>) => Promise<ConfirmationDescription>;
40
+ readonly labels: (actor: OntologyActor, action: string, input: Readonly<Record<string, unknown>>) => Promise<ActionAnnotations>;
41
+ readonly describe?: (actor: OntologyActor, action: string, input: Readonly<Record<string, unknown>>) => Promise<ConfirmationDescription>;
37
42
  readonly texts?: () => TelegramTexts;
38
43
  readonly state?: StateHandle<Record<string, Confirmation>>;
39
44
  readonly now?: () => number;
@@ -5,9 +5,10 @@ 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 { i as memberIdOf } from "../../_chunks/principal-DFcIfKSa.mjs";
8
+ import { i as memberIdOf } from "../../_chunks/principal-69qoDdqy.mjs";
9
9
  import { confirmationCopy, confirmationLocale, renderConfirmation } from "./telegram/confirmation-text.mjs";
10
10
  import { chatPostCard } from "./telegram/chat-post-card.mjs";
11
+ import { actorOf, member } from "./actor.mjs";
11
12
  import { chatPostDigest, isChatPost } from "./chat-post-confirmation.mjs";
12
13
  import { createHash } from "node:crypto";
13
14
  import { defineState } from "eve/context";
@@ -35,7 +36,18 @@ function actionApproval(options) {
35
36
  });
36
37
  return {
37
38
  async request(ctx) {
38
- const memberId = memberIdOf(ctx.session.auth.current);
39
+ const actor = actorOf(ctx.session.auth.current);
40
+ if (actor?.kind === "task") {
41
+ if (isChatPost(ctx.toolName)) return denied("Chat posting is unavailable in a task.");
42
+ try {
43
+ await options.labels(actor, ctx.toolName, ctx.toolInput ?? {});
44
+ } catch (error) {
45
+ console.warn("The ontology action permission check failed.", error);
46
+ return denied("The action is unavailable or the task's role does not permit it.");
47
+ }
48
+ return "approved";
49
+ }
50
+ const memberId = actor?.memberId;
39
51
  if (memberId === void 0) return denied("A paired member is required.");
40
52
  const present = (run) => options.texts === void 0 ? run() : withTelegramTexts(options.texts(), run, () => confirmationLocale(ctx.session.auth.current));
41
53
  const hash = inputHash(ctx.toolInput ?? {});
@@ -47,7 +59,7 @@ function actionApproval(options) {
47
59
  }
48
60
  let labels;
49
61
  try {
50
- labels = await options.labels(memberId, ctx.toolName, ctx.toolInput ?? {});
62
+ labels = await options.labels(member(memberId), ctx.toolName, ctx.toolInput ?? {});
51
63
  } catch (error) {
52
64
  console.warn("The ontology action permission check failed.", error);
53
65
  return denied("The action is unavailable or the member no longer has permission.");
@@ -57,7 +69,7 @@ function actionApproval(options) {
57
69
  const posting = isChatPost(ctx.toolName);
58
70
  let description;
59
71
  if (posting) try {
60
- description = await options.describe?.(memberId, ctx.toolName, businessInput);
72
+ description = await options.describe?.(member(memberId), ctx.toolName, businessInput);
61
73
  const preview = description?.chatPost;
62
74
  const source = ctx.session.auth.current?.attributes;
63
75
  if (description === void 0 || preview === void 0 || typeof source?.chatId !== "string" || source.chatId === preview.externalId) return denied("A complete preview for a different chat is required. Reply normally in this chat.");
@@ -84,7 +96,7 @@ function actionApproval(options) {
84
96
  if (saved !== void 0) return denied("The original call is waiting for its Confirm / Cancel buttons.");
85
97
  if (!posting && !needsConfirmation(labels)) return "approved";
86
98
  try {
87
- if (!posting) description = await options.describe?.(memberId, ctx.toolName, businessInput);
99
+ if (!posting) description = await options.describe?.(member(memberId), ctx.toolName, businessInput);
88
100
  if (description === void 0) throw new Error("A trusted confirmation description is required.");
89
101
  } catch (error) {
90
102
  console.warn("The confirmation description could not be read.", error);
@@ -4,17 +4,27 @@ 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 { agentTaskIdOf } from "./agent-task.mjs";
7
8
  import { ENDS_CONVERSATION_INSTRUCTIONS } from "./group-routing/conversation-prompt.mjs";
8
- import { i as memberIdOf } from "../../_chunks/principal-DFcIfKSa.mjs";
9
+ import { i as memberIdOf } from "../../_chunks/principal-69qoDdqy.mjs";
9
10
  import { n as contentText, t as CONTENT_CAPS } from "../../_chunks/content-cDtVnr8Y.mjs";
10
11
  import { conversationEvent, conversations, sessionFailureEventId, summarizeArguments } from "./conversations.mjs";
11
12
  import { parseAddress } from "./telegram/sessions.mjs";
12
13
  import { completionKind } from "./telegram/silent.mjs";
13
14
  import { recordModelServiceAlert } from "./model-service-alert.mjs";
14
- import { decide } from "eve/ai";
15
15
  import { call } from "eve/events";
16
+ import { decide } from "eve/ai";
16
17
  function conversationCoordinates(context) {
17
18
  const principal = context.session.auth.current;
19
+ const taskId = agentTaskIdOf(principal);
20
+ if (taskId !== void 0) return {
21
+ provider: "task",
22
+ chatId: taskId,
23
+ chatType: "private",
24
+ threadId: null,
25
+ participantIds: [],
26
+ senderId: principal?.principalId ?? null
27
+ };
18
28
  const telegram = principal?.authenticator === "segmentstream-telegram" || context.channel.kind?.includes("telegram");
19
29
  const address = context.channel.continuationToken;
20
30
  const attributes = principal?.attributes;
@@ -4,9 +4,10 @@ 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 { appOrigin } from "./environment.mjs";
8
7
  import { redactDiagnostic } from "./diagnostics.mjs";
8
+ import { appOrigin } from "./environment.mjs";
9
9
  import { runtimeToken } from "./runtime-token.mjs";
10
+ import { member } from "./actor.mjs";
10
11
  import { chatPostDigest, isChatPost } from "./chat-post-confirmation.mjs";
11
12
  import { inputHash } from "./confirmations.mjs";
12
13
  import { ontologyTools } from "./ontology-tools.mjs";
@@ -36,11 +37,11 @@ function deferredConfirmations(options = {}) {
36
37
  async function runDeferredAction(record, options) {
37
38
  if (record.inputHash !== inputHash(record.data.input)) throw new Error("The confirmed input changed.");
38
39
  const tools = ontologyTools(options);
39
- await tools.labels(record.memberId, record.data.action, record.data.input);
40
+ await tools.labels(member(record.memberId), record.data.action, record.data.input);
40
41
  const preview = record.data.confirmation.description?.chatPost;
41
42
  if (isChatPost(record.data.action)) {
42
43
  const input = Object.fromEntries(Object.entries(record.data.input).filter(([key]) => key !== "_confirmation"));
43
- const current = (await tools.describe(record.memberId, record.data.action, input)).chatPost;
44
+ const current = (await tools.describe(member(record.memberId), record.data.action, input)).chatPost;
44
45
  if (!preview || !current || chatPostDigest(preview) !== chatPostDigest(current)) throw new Error("The confirmed chat destination or attribution changed.");
45
46
  }
46
47
  const envelope = {
@@ -65,7 +66,7 @@ async function runDeferredAction(record, options) {
65
66
  const result = await ontologyTools({
66
67
  ...options,
67
68
  envelope
68
- }).rpc(record.memberId, "tools/call", {
69
+ }).rpc(member(record.memberId), "tools/call", {
69
70
  name: record.data.action.split("__").at(-1),
70
71
  arguments: record.data.input
71
72
  });
@@ -12,8 +12,8 @@ import { routeMessage } from "./router.mjs";
12
12
  import { archivedRoutingMessages } from "./routing-context.mjs";
13
13
  import { routingBurst } from "./burst.mjs";
14
14
  import { conversationCoordinates } from "../conversation-runtime.mjs";
15
- import { decide } from "eve/ai";
16
15
  import { createHash } from "node:crypto";
16
+ import { decide } from "eve/ai";
17
17
  function conversationDequeueGuard(archive = conversations(), classifier = decide) {
18
18
  return async (event, ctx) => {
19
19
  const auth = ctx.session.auth.current;
@@ -2,33 +2,33 @@ import type { SessionAuthContext } from "eve/context";
2
2
  import { type McpClientConnectionDefinition } from "eve/connections";
3
3
  import { type Environment } from "./environment.js";
4
4
  import { type RuntimeToken } from "./runtime-token.js";
5
+ import { type OntologyActor } from "./actor.js";
5
6
  import type { TelegramTexts } from "./telegram/texts.js";
6
7
  /**
7
8
  * The ontology as tools: an MCP connection to the workspace app's `/mcp`
8
9
  * (`ontology-mcp-design.md`). The agent authenticates as the deployment with its Vercel OIDC token
9
- * and names the member it acts for in `X-SegmentStream-Act-As`; the ontology service checks that
10
- * the member is active and has a live pairing, lists only the tools the member may run, and
11
- * records the member in history.
10
+ * and names whom it acts for: the member in `X-SegmentStream-Act-As`, or a task's secret in
11
+ * `X-SegmentStream-Agent-Task` (`agent-tasks-spec.md`). The ontology service checks that the member
12
+ * is active and has a live pairing, or that the task is live, lists only the tools that user may
13
+ * run, and records them in history.
12
14
  */
13
- /** The header that names the member a runtime caller acts for. */
14
- export declare const ACT_AS_HEADER = "X-SegmentStream-Act-As";
15
- /** The session has no member to act for, so the ontology must not be reached. */
16
- export declare class MemberRequiredError extends Error {
17
- readonly name = "MemberRequiredError";
18
- }
15
+ export { ACT_AS_HEADER } from "./actor.js";
19
16
  /**
20
- * The headers that name the member, from the session principal of the turn. The connection fails
21
- * without one, and fails when the turn's member is not the one the connection instance was opened
22
- * for, so a client opened for one member never serves another.
17
+ * The headers that name whom the turn acts for, from its session principal. The connection fails
18
+ * without a member or task, and fails when the turn's actor is not the one the connection instance
19
+ * was opened for, so a client opened for one member or task never serves another.
23
20
  */
24
21
  export declare function actAsHeaders(input: {
25
22
  readonly principal: SessionAuthContext | null;
26
- readonly expectedMemberId: string;
23
+ readonly expected: OntologyActor;
24
+ readonly secret?: (taskId: string) => string | undefined;
27
25
  }): Record<string, string>;
28
26
  /**
29
- * The connection for one turn, or `null` when the turn has no member principal: an unpaired
30
- * caller never reaches the ontology. The instance key names the member, so eve opens a separate
31
- * client per member even when members share a group chat's session.
27
+ * The connection for one turn, or `null` when the turn has no member or task principal: an
28
+ * unpaired caller never reaches the ontology. The instance key names the member or the task, so
29
+ * eve opens a separate client per member even when members share a group chat's session, and a
30
+ * task never shares a client with a member. A task session negotiates neither chat posting nor the
31
+ * chat's runtime source, and does not offer `chat_post_message`.
32
32
  */
33
33
  export declare function ontologyConnection(input: {
34
34
  readonly principal: SessionAuthContext | null;
@@ -40,4 +40,6 @@ export declare function ontologyConnection(input: {
40
40
  /** Trusted deployment capability (or authenticated handshake), never model input. */
41
41
  readonly chatPosting?: boolean;
42
42
  readonly runtimeSource?: boolean;
43
+ /** Reads a task's live secret; the session's task state unless a test passes another. */
44
+ readonly secret?: (taskId: string) => string | undefined;
43
45
  }): McpClientConnectionDefinition | null;
@@ -5,48 +5,68 @@ 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 { c as compatibleRuntimeEnvelope, i as memberIdOf, r as identityProviderOf, u as runtimeEnvelope } from "../../_chunks/principal-DFcIfKSa.mjs";
9
8
  import { runtimeToken, tokenExpiry } from "./runtime-token.mjs";
10
- import { CHAT_POST_ENVELOPE, chatPostDigest, chatPostOperationId, isChatPost } from "./chat-post-confirmation.mjs";
9
+ import { c as compatibleRuntimeEnvelope, u as runtimeEnvelope } from "../../_chunks/principal-69qoDdqy.mjs";
10
+ import { ACT_AS_HEADER, ActorRequiredError, actorHeaders, actorKey, actorOf } from "./actor.mjs";
11
+ import { CHAT_POST_ENVELOPE, CHAT_POST_TOOL, chatPostDigest, chatPostOperationId, isChatPost } from "./chat-post-confirmation.mjs";
11
12
  import { actionApproval, confirmations } from "./confirmations.mjs";
12
13
  import { ontologyTools } from "./ontology-tools.mjs";
13
14
  import { defineMcpClientConnection } from "eve/connections";
14
- const ACT_AS_HEADER = "X-SegmentStream-Act-As";
15
- const DESCRIPTION = "The company's ontology: its records, their history, and the actions that change them, narrowed to what the member you act for may read and do. Use describe for the compact entity index or discover for metadata search, then describe relevant refs for complete definitions. Call me for the member. Older ontologies without discover provide the legacy catalog through describe.";
16
- var MemberRequiredError = class extends Error {
17
- name = "MemberRequiredError";
18
- };
15
+ const DESCRIPTION = "The company's ontology: its records, their history, and the actions that change them, narrowed to what the user you act for may read and do. Use describe for the compact entity index or discover for metadata search, then describe relevant refs for complete definitions. Call me for that user. Older ontologies without discover provide the legacy catalog through describe.";
19
16
  function actAsHeaders(input) {
20
- const memberId = memberIdOf(input.principal);
21
- if (memberId === void 0) throw new MemberRequiredError("The ontology is reachable only for a member paired with this agent.");
22
- if (memberId !== input.expectedMemberId) throw new MemberRequiredError("The ontology connection was opened for another member.");
23
- const provider = identityProviderOf(input.principal);
24
- return {
25
- [ACT_AS_HEADER]: memberId,
26
- ...provider === void 0 ? {} : { "X-SegmentStream-Identity-Provider": provider }
27
- };
17
+ const actor = actorOf(input.principal);
18
+ if (actor === void 0) throw new ActorRequiredError("The ontology is reachable only for a member paired with this agent or a live task.");
19
+ if (actorKey(actor) !== actorKey(input.expected)) throw new ActorRequiredError("The ontology connection was opened for another member or task.");
20
+ return actorHeaders({
21
+ actor,
22
+ principal: input.principal,
23
+ secret: input.secret
24
+ });
28
25
  }
29
26
  function ontologyConnection(input) {
30
- const memberId = memberIdOf(input.principal);
31
- if (memberId === void 0) return null;
27
+ const actor = actorOf(input.principal);
28
+ if (actor === void 0) return null;
32
29
  const origin = appOrigin(input.environment ?? process.env);
33
30
  const token = input.token ?? runtimeToken;
34
- const chatProtocol = input.chatPosting ? ":chat-post-v1" : "";
35
- const sourceProtocol = input.runtimeSource ? ":runtime-source-v1" : "";
36
- return defineMcpClientConnection({
31
+ const headers = (context) => actAsHeaders({
32
+ principal: context.session.auth.current,
33
+ expected: actor,
34
+ secret: input.secret
35
+ });
36
+ const common = {
37
37
  url: `${origin}/mcp`,
38
38
  protocolVersionDiscovery: false,
39
39
  description: DESCRIPTION,
40
- instanceKey: `${origin}/mcp#member:${memberId}${chatProtocol}${sourceProtocol}`,
41
40
  approval: actionApproval({
42
41
  ...ontologyTools(input),
43
42
  texts: input.texts
44
43
  }),
44
+ auth: {
45
+ principalType: "app",
46
+ getToken: async () => {
47
+ const value = await token();
48
+ const expiresAt = tokenExpiry(value);
49
+ return expiresAt === void 0 ? { token: value } : {
50
+ token: value,
51
+ expiresAt
52
+ };
53
+ }
54
+ },
55
+ headers
56
+ };
57
+ if (actor.kind === "task") return defineMcpClientConnection({
58
+ ...common,
59
+ instanceKey: `${origin}/mcp#task:${actor.taskId}`,
60
+ tools: { block: [CHAT_POST_TOOL] }
61
+ });
62
+ const memberId = actor.memberId;
63
+ const chatProtocol = input.chatPosting ? ":chat-post-v1" : "";
64
+ const sourceProtocol = input.runtimeSource ? ":runtime-source-v1" : "";
65
+ return defineMcpClientConnection({
66
+ ...common,
67
+ instanceKey: `${origin}/mcp#member:${memberId}${chatProtocol}${sourceProtocol}`,
45
68
  ...input.chatPosting || input.runtimeSource ? { toolCall: { providedArguments: { [CHAT_POST_ENVELOPE]: (ctx) => {
46
- actAsHeaders({
47
- principal: ctx.session.auth.current,
48
- expectedMemberId: memberId
49
- });
69
+ headers(ctx);
50
70
  const base = compatibleRuntimeEnvelope(runtimeEnvelope({
51
71
  principal: ctx.session.auth.current,
52
72
  sessionId: ctx.session.id,
@@ -70,22 +90,7 @@ function ontologyConnection(input) {
70
90
  digest: chatPostDigest(preview)
71
91
  }
72
92
  };
73
- } } } } : {},
74
- auth: {
75
- principalType: "app",
76
- getToken: async () => {
77
- const value = await token();
78
- const expiresAt = tokenExpiry(value);
79
- return expiresAt === void 0 ? { token: value } : {
80
- token: value,
81
- expiresAt
82
- };
83
- }
84
- },
85
- headers: (context) => actAsHeaders({
86
- principal: context.session.auth.current,
87
- expectedMemberId: memberId
88
- })
93
+ } } } } : {}
89
94
  });
90
95
  }
91
- export { ACT_AS_HEADER, MemberRequiredError, actAsHeaders, ontologyConnection };
96
+ export { ACT_AS_HEADER, actAsHeaders, ontologyConnection };