@segmentstream/agent 0.20.2 → 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.
- package/README.md +50 -5
- package/dist/_chunks/{build-metadata-CftTwBkw.mjs → build-metadata-kBu1knj6.mjs} +1 -1
- package/dist/_chunks/{principal-DFcIfKSa.mjs → principal-69qoDdqy.mjs} +2 -0
- package/dist/extension/channels/tasks.d.ts +7 -0
- package/dist/extension/channels/tasks.mjs +9 -0
- package/dist/extension/connections/ontology.d.ts +1 -1
- package/dist/extension/hooks/authorization.mjs +1 -1
- package/dist/extension/instructions/conversations.d.ts +4 -1
- package/dist/extension/instructions/conversations.mjs +2 -1
- package/dist/extension/lib/actor.d.ts +39 -0
- package/dist/extension/lib/actor.mjs +48 -0
- package/dist/extension/lib/agent-task-channel.d.ts +45 -0
- package/dist/extension/lib/agent-task-channel.mjs +201 -0
- package/dist/extension/lib/agent-task.d.ts +127 -0
- package/dist/extension/lib/agent-task.mjs +183 -0
- package/dist/extension/lib/attachment-tools.mjs +1 -1
- package/dist/extension/lib/attachments.mjs +1 -1
- package/dist/extension/lib/build-metadata.mjs +1 -1
- package/dist/extension/lib/chat-post-confirmation.d.ts +2 -0
- package/dist/extension/lib/chat-post-confirmation.mjs +3 -2
- package/dist/extension/lib/confirmation-outcome.d.ts +1 -1
- package/dist/extension/lib/confirmations.d.ts +8 -3
- package/dist/extension/lib/confirmations.mjs +17 -5
- package/dist/extension/lib/conversation-runtime.mjs +12 -2
- package/dist/extension/lib/deferred-confirmations.mjs +5 -4
- package/dist/extension/lib/group-routing/dequeue.mjs +1 -1
- package/dist/extension/lib/ontology-connection.d.ts +18 -16
- package/dist/extension/lib/ontology-connection.mjs +47 -42
- package/dist/extension/lib/ontology-file-call.mjs +26 -21
- package/dist/extension/lib/ontology-tools.d.ts +12 -9
- package/dist/extension/lib/ontology-tools.mjs +42 -33
- package/dist/extension/lib/runtime-envelope.mjs +1 -1
- package/dist/extension/lib/sandbox-session.mjs +1 -1
- package/dist/extension/lib/stored-files.mjs +3 -2
- package/dist/extension/lib/telegram/adapter.mjs +5 -5
- package/dist/extension/lib/telegram/attachments-send.mjs +4 -4
- package/dist/extension/lib/telegram/callbacks.mjs +1 -1
- package/dist/extension/lib/telegram/confirmation-expiry.mjs +2 -2
- package/dist/extension/lib/telegram/confirmations.mjs +2 -2
- package/dist/extension/lib/telegram/deferred-confirmations.mjs +1 -1
- package/dist/extension/lib/telegram/durable-typing.mjs +1 -1
- package/dist/extension/lib/telegram/files.mjs +1 -1
- package/dist/extension/lib/telegram/health.mjs +1 -1
- package/dist/extension/lib/telegram/held-approvals.d.ts +1 -1
- package/dist/extension/lib/telegram/inbound.mjs +1 -1
- package/dist/extension/lib/telegram/lifecycle.mjs +6 -5
- package/dist/extension/lib/telegram/principal.d.ts +4 -1
- package/dist/extension/lib/telegram/principal.mjs +1 -1
- package/dist/extension/lib/telegram/silent.mjs +1 -1
- package/dist/extension/lib/telegram/structured.mjs +1 -1
- package/dist/extension/lib/telegram/turn-errors.mjs +1 -1
- package/dist/extension/lib/telegram/workspace-action.mjs +4 -3
- package/dist/extension/model.mjs +2 -2
- package/dist/extension/tools/attachments_discard.d.ts +1 -7
- package/dist/extension/tools/attachments_discard.mjs +3 -1
- package/dist/extension/tools/attachments_list.d.ts +1 -7
- package/dist/extension/tools/attachments_list.mjs +3 -1
- package/dist/extension/tools/attachments_promote.d.ts +1 -4
- package/dist/extension/tools/attachments_promote.mjs +8 -6
- package/dist/extension/tools/attachments_read.d.ts +1 -8
- package/dist/extension/tools/attachments_read.mjs +3 -1
- package/dist/extension/tools/attachments_send.d.ts +1 -1
- package/dist/extension/tools/attachments_send.mjs +3 -1
- package/dist/extension/tools/attachments_table.d.ts +1 -3
- package/dist/extension/tools/attachments_table.mjs +3 -1
- package/dist/extension/tools/questions_ask.mjs +14 -1
- 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 "./
|
|
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-
|
|
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) ===
|
|
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: "
|
|
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
|
-
/**
|
|
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: (
|
|
36
|
-
readonly describe?: (
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
|
|
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
|
|
21
|
-
* without
|
|
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
|
|
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
|
|
30
|
-
* caller never reaches the ontology. The instance key names the member
|
|
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 {
|
|
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
|
|
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
|
|
21
|
-
if (
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
31
|
-
if (
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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,
|
|
96
|
+
export { ACT_AS_HEADER, actAsHeaders, ontologyConnection };
|