@segmentstream/agent 0.8.5 → 0.8.7
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 +30 -0
- package/dist/_chunks/{build-metadata-BYl-5kFi.mjs → build-metadata-CR18wzcH.mjs} +1 -1
- package/dist/extension/config.mjs +15 -2
- package/dist/extension/instructions.md +9 -1
- package/dist/extension/lib/attachment-tables.mjs +3 -2
- package/dist/extension/lib/build-metadata.mjs +1 -1
- package/dist/extension/lib/ontology-gateway.d.ts +20 -0
- package/dist/extension/lib/ontology-gateway.mjs +44 -0
- package/dist/extension/lib/telegram/adapter.mjs +13 -5
- package/dist/extension/lib/telegram/confirmation-text.d.ts +2 -0
- package/dist/extension/lib/telegram/confirmation-text.mjs +6 -1
- package/dist/extension/lib/telegram/confirmations.mjs +3 -3
- package/dist/extension/lib/telegram/health.d.ts +4 -0
- package/dist/extension/lib/telegram/health.mjs +3 -2
- package/dist/extension/lib/telegram/held-approvals.d.ts +27 -0
- package/dist/extension/lib/telegram/held-approvals.mjs +51 -0
- package/dist/extension/lib/telegram/inbound-request.d.ts +1 -1
- package/dist/extension/lib/telegram/inbound.d.ts +2 -0
- package/dist/extension/lib/telegram/inbound.mjs +35 -1
- package/dist/extension/lib/telegram/lifecycle.d.ts +3 -0
- package/dist/extension/lib/telegram/lifecycle.mjs +5 -2
- package/dist/extension/lib/telegram/observed.d.ts +1 -1
- package/dist/extension/lib/telegram/sessions.d.ts +4 -0
- package/dist/extension/lib/telegram/silent.mjs +10 -2
- package/dist/extension/tools/ontology.d.ts +24 -1
- package/dist/extension/tools/ontology.mjs +113 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -606,3 +606,33 @@ HTTP 503 with a correlation reference before durable queue admission. Telegram
|
|
|
606
606
|
can redeliver the same source; the hook and attachment reservation must preserve
|
|
607
607
|
idempotent source membership, storage results and the single acknowledgement.
|
|
608
608
|
Such a failure must not become an HTTP success with no queued work.
|
|
609
|
+
|
|
610
|
+
### Legacy held approvals
|
|
611
|
+
|
|
612
|
+
Deferred mode supplies four eager tools: `segmentstream__ontology_discover`,
|
|
613
|
+
`segmentstream__ontology_describe`, `segmentstream__ontology_inspect` and `segmentstream__ontology_call`. Registration
|
|
614
|
+
does not fetch the full ontology catalog. Discovery and descriptions remain
|
|
615
|
+
permission-filtered reads; descriptions provide the exact `tool_name` and input
|
|
616
|
+
schema for execution. Calls retain the same member-bound deferred confirmation
|
|
617
|
+
boundary. Metadata failures are reported by the tool instead of removing the
|
|
618
|
+
entire ontology surface from the turn.
|
|
619
|
+
The inspector exposes the full member-permitted input schemas for ordinary record
|
|
620
|
+
find/get/history operations on demand. Describe/discover accept `expected_hash`
|
|
621
|
+
to reject mixed ontology versions.
|
|
622
|
+
|
|
623
|
+
With `attachmentLifecycle.contextVersion: 2`, lifecycle data includes `source_at` when Telegram provides its
|
|
624
|
+
original message timestamp, so a workspace can group related uploads despite
|
|
625
|
+
serial webhook delivery delays. A completion hook receives the model's `reply`
|
|
626
|
+
alongside its intake key, allowing workspace-owned status copy to retain an
|
|
627
|
+
actionable explanation. Reply delivery carries the owning turn's reply anchor
|
|
628
|
+
explicitly after restore. Omitting the version preserves existing closed lifecycle
|
|
629
|
+
data schemas; workspaces opt in only after their hook accepts the additional fields.
|
|
630
|
+
|
|
631
|
+
Explicit `confirmations.mode: "held"` overrides the deferred default. Switching a package version
|
|
632
|
+
does not migrate already-running held turns. In deferred mode the Telegram adapter reads the owning session's
|
|
633
|
+
recorded approval inputs and requests cancellation of that exact turn before admitting a new
|
|
634
|
+
addressed message. It never approves the old call or reuses the old member's authorization.
|
|
635
|
+
Cancellation failure requests webhook replay; terminal/resolved approvals are left alone.
|
|
636
|
+
Deferred mode retains its ten-minute database deadline and minute expiry sweep. Runtime health
|
|
637
|
+
reports the effective confirmation mode and attachment-lifecycle presence behind its existing
|
|
638
|
+
runtime authentication; source pins and public health alone are not settings-adoption proof.
|
|
@@ -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.8.
|
|
7
|
+
var version = "0.8.7";
|
|
8
8
|
function agentBuild(environment = process.env) {
|
|
9
9
|
return {
|
|
10
10
|
agent: version,
|
|
@@ -95,13 +95,25 @@ function telegramSettings(value, issues) {
|
|
|
95
95
|
const lifecycle = section({
|
|
96
96
|
value: telegram.attachmentLifecycle,
|
|
97
97
|
path: [...path, "attachmentLifecycle"],
|
|
98
|
-
allowed: [
|
|
98
|
+
allowed: [
|
|
99
|
+
"entity",
|
|
100
|
+
"action",
|
|
101
|
+
"contextVersion"
|
|
102
|
+
],
|
|
99
103
|
issues
|
|
100
104
|
});
|
|
101
105
|
if (lifecycle !== void 0 && ["entity", "action"].some((key) => typeof lifecycle[key] !== "string" || !/^(?:[a-z][a-zA-Z0-9]*|[a-z][a-z0-9]*(?:_[a-z0-9]+)*)$/.test(String(lifecycle[key])))) issues.push({
|
|
102
106
|
message: "must name a valid ontology entity and action",
|
|
103
107
|
path: [...path, "attachmentLifecycle"]
|
|
104
108
|
});
|
|
109
|
+
if (lifecycle?.contextVersion !== void 0 && lifecycle.contextVersion !== 2) issues.push({
|
|
110
|
+
message: "must be 2 when specified",
|
|
111
|
+
path: [
|
|
112
|
+
...path,
|
|
113
|
+
"attachmentLifecycle",
|
|
114
|
+
"contextVersion"
|
|
115
|
+
]
|
|
116
|
+
});
|
|
105
117
|
return typeof connection === "string" && isTelegramPolicy(policy) ? {
|
|
106
118
|
connection,
|
|
107
119
|
policy,
|
|
@@ -109,7 +121,8 @@ function telegramSettings(value, issues) {
|
|
|
109
121
|
additionalOwnOrigins,
|
|
110
122
|
...lifecycle === void 0 ? {} : { attachmentLifecycle: {
|
|
111
123
|
entity: String(lifecycle.entity),
|
|
112
|
-
action: String(lifecycle.action)
|
|
124
|
+
action: String(lifecycle.action),
|
|
125
|
+
...lifecycle.contextVersion === 2 ? { contextVersion: 2 } : {}
|
|
113
126
|
} }
|
|
114
127
|
} : null;
|
|
115
128
|
}
|
|
@@ -3,7 +3,15 @@
|
|
|
3
3
|
You are the workspace's agent on SegmentStream. Every conversation runs as the member who is
|
|
4
4
|
talking to you, and you act with exactly their permissions.
|
|
5
5
|
|
|
6
|
-
- The company's records live in the ontology
|
|
6
|
+
- The company's records live in the ontology. With deferred confirmations, use
|
|
7
|
+
`segmentstream__ontology_discover` to search permitted definitions,
|
|
8
|
+
`segmentstream__ontology_describe` to inspect their refs and schemas, and
|
|
9
|
+
`segmentstream__ontology_call` to run their exact `tool_name` with schema-matching arguments.
|
|
10
|
+
For record reads (find/get/history), use `segmentstream__ontology_inspect` to discover
|
|
11
|
+
exact MCP names and input schemas. Pass returned ontology hashes as `expected_hash`
|
|
12
|
+
on subsequent describe/discover requests so a redeployment cannot mix definitions.
|
|
13
|
+
These tools are directly available; do not search a connection namespace to find them.
|
|
14
|
+
With held confirmations the same operations are reached through `segmentstream__ontology`.
|
|
7
15
|
Call `me` when you need to check whom you act for. For an unfamiliar task, call `describe`
|
|
8
16
|
without a ref for a compact entity index, or `discover` to search names and descriptions.
|
|
9
17
|
Inspect relevant `entity:`, `action:`, or `measure:` refs with `describe` for their complete
|
|
@@ -115,8 +115,9 @@ function present(value) {
|
|
|
115
115
|
}
|
|
116
116
|
function detectedHeader(rows) {
|
|
117
117
|
const candidates = rows.slice(0, 50);
|
|
118
|
-
const width = candidates.reduce((maximum, row) => Math.max(maximum, row.
|
|
119
|
-
const
|
|
118
|
+
const width = candidates.reduce((maximum, row) => Math.max(maximum, row.findLastIndex(present) + 1), 0);
|
|
119
|
+
const minimumLabels = Math.min(width, 2);
|
|
120
|
+
const labels = candidates.findIndex((row) => row.filter(present).length >= minimumLabels && row.some(present) && row.filter(present).every((value) => typeof value === "string" && numeric(value) === null && dateValue(value, "day_first") === null && dateValue(value, "month_first") === null));
|
|
120
121
|
if (labels >= 0) return labels;
|
|
121
122
|
return -1;
|
|
122
123
|
}
|
|
@@ -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-CR18wzcH.mjs";
|
|
8
8
|
export { agentBuild };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ToolContext } from "eve/tools";
|
|
2
|
+
import { ontologyTools } from "./ontology-tools.js";
|
|
3
|
+
type Options = Omit<NonNullable<Parameters<typeof ontologyTools>[0]>, "principal">;
|
|
4
|
+
/** Metadata is requested only when the model uses it, never while registering its tools. */
|
|
5
|
+
export declare function ontologyMetadata(memberId: string, operation: "describe" | "discover", input: Record<string, unknown>, context: ToolContext, options?: Options): Promise<Record<string, unknown>>;
|
|
6
|
+
/** Ordinary record operations are not part of entity/action discovery. Inspect their
|
|
7
|
+
* real member-filtered MCP schemas on demand, never during dynamic registration. */
|
|
8
|
+
export declare function ontologyInspect(memberId: string, input: {
|
|
9
|
+
name?: string;
|
|
10
|
+
query?: string;
|
|
11
|
+
after?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
}, context: ToolContext, options?: Options): Promise<{
|
|
14
|
+
next_after?: string | undefined;
|
|
15
|
+
tools: {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
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 { memberIdOf } from "./telegram/principal.mjs";
|
|
8
|
+
import { object } from "./confirmation-description.mjs";
|
|
9
|
+
import { ontologyTools } from "./ontology-tools.mjs";
|
|
10
|
+
async function ontologyMetadata(memberId, operation, input, context, options = {}) {
|
|
11
|
+
const principal = context.session.auth.current;
|
|
12
|
+
if (memberIdOf(principal) !== memberId) throw new Error("The ontology tool belongs to another member.");
|
|
13
|
+
const result = await ontologyTools({
|
|
14
|
+
...options,
|
|
15
|
+
principal
|
|
16
|
+
}).rpc(memberId, "tools/call", {
|
|
17
|
+
name: operation,
|
|
18
|
+
arguments: input
|
|
19
|
+
});
|
|
20
|
+
return result.structuredContent === void 0 ? result : object(result.structuredContent);
|
|
21
|
+
}
|
|
22
|
+
async function ontologyInspect(memberId, input, context, options = {}) {
|
|
23
|
+
const principal = context.session.auth.current;
|
|
24
|
+
if (memberIdOf(principal) !== memberId) throw new Error("The ontology tool belongs to another member.");
|
|
25
|
+
const tools = (await ontologyTools({
|
|
26
|
+
...options,
|
|
27
|
+
principal
|
|
28
|
+
}).list(memberId)).flatMap((tool) => typeof tool.name === "string" ? [{
|
|
29
|
+
...tool,
|
|
30
|
+
name: tool.name,
|
|
31
|
+
description: String(tool.description ?? "")
|
|
32
|
+
}] : []);
|
|
33
|
+
const words = input.query?.toLowerCase().split(/\s+/u).filter(Boolean) ?? [];
|
|
34
|
+
const matched = tools.filter((tool) => input.name !== void 0 ? tool.name === input.name : words.every((word) => `${tool.name} ${tool.description}`.toLowerCase().includes(word))).sort((a, b) => a.name.localeCompare(b.name));
|
|
35
|
+
if (input.name !== void 0 && matched.length === 0) throw new Error("The requested operation is not available to the current member.");
|
|
36
|
+
const remaining = matched.filter((tool) => input.after === void 0 || tool.name.localeCompare(input.after) > 0);
|
|
37
|
+
const limit = Math.min(50, Math.max(1, input.limit ?? 20));
|
|
38
|
+
const page = remaining.slice(0, limit);
|
|
39
|
+
return {
|
|
40
|
+
tools: page,
|
|
41
|
+
...remaining.length > limit ? { next_after: page.at(-1).name } : {}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export { ontologyInspect, ontologyMetadata };
|
|
@@ -8,7 +8,7 @@ import { manifestOf } from "../../config.mjs";
|
|
|
8
8
|
import { appOrigin, channelsEnabled, proxyEnvironment, runtimeIdentityEnvironment, webhookSecret } from "../environment.mjs";
|
|
9
9
|
import { conversationLanguage, telegramPrincipal } from "./principal.mjs";
|
|
10
10
|
import { chooseModel } from "../../model.mjs";
|
|
11
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
11
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-CR18wzcH.mjs";
|
|
12
12
|
import { confirmationLocale } from "./confirmation-text.mjs";
|
|
13
13
|
import { telegramSendScope } from "./send-queue.mjs";
|
|
14
14
|
import { d as failureText, h as withDeliveryLocale, i as withReplacedText, m as safeTelegramFetch, n as currentInboundRequest, p as reportTelegramError, r as runInboundRequest, t as InboundRequest } from "../../../_chunks/inbound-request-B98tr4Pd.mjs";
|
|
@@ -166,6 +166,7 @@ function segmentstreamTelegram(options) {
|
|
|
166
166
|
request: currentInboundRequest(),
|
|
167
167
|
dependencies: {
|
|
168
168
|
policy: settings().policy,
|
|
169
|
+
recoverHeldApprovals: options.confirmationMode?.() === "deferred",
|
|
169
170
|
...settings().attachmentLifecycle === void 0 ? {} : { lifecycle: (principal, memberId, message) => attachmentLifecycle({
|
|
170
171
|
configuration: settings().attachmentLifecycle,
|
|
171
172
|
principal,
|
|
@@ -252,7 +253,7 @@ function segmentstreamTelegram(options) {
|
|
|
252
253
|
} catch (error) {
|
|
253
254
|
recordGateDecision("inbound_message_failed");
|
|
254
255
|
const errorId = reportTelegramError("inbound_message_failed", error, { chatId: message.chat.id });
|
|
255
|
-
if (inbound?.lifecycleKey !== void 0) inbound.retryReference = errorId;
|
|
256
|
+
if (inbound?.retryReference !== void 0 || inbound?.lifecycleKey !== void 0) inbound.retryReference = errorId;
|
|
256
257
|
else await context.telegram.post(failureText(errorId));
|
|
257
258
|
return null;
|
|
258
259
|
} finally {
|
|
@@ -318,7 +319,10 @@ function segmentstreamTelegram(options) {
|
|
|
318
319
|
messageThreadId: channel.telegram.messageThreadId,
|
|
319
320
|
from: { id: principal.subject ?? "" }
|
|
320
321
|
}
|
|
321
|
-
})("completed", {
|
|
322
|
+
})("completed", {
|
|
323
|
+
key: taskId,
|
|
324
|
+
...configuration.contextVersion === 2 ? { reply: event.message } : {}
|
|
325
|
+
});
|
|
322
326
|
if (result.message !== void 0 || result.complete === false) event = {
|
|
323
327
|
...event,
|
|
324
328
|
message: result.message ?? ""
|
|
@@ -394,7 +398,11 @@ function segmentstreamTelegram(options) {
|
|
|
394
398
|
chats,
|
|
395
399
|
enabled: () => channelsEnabled(environment),
|
|
396
400
|
configured: () => options.settings() !== null,
|
|
397
|
-
verify: () => verify ??= runtimeTokenVerifier(runtimeIdentityEnvironment(environment))
|
|
401
|
+
verify: () => verify ??= runtimeTokenVerifier(runtimeIdentityEnvironment(environment)),
|
|
402
|
+
configuration: () => ({
|
|
403
|
+
confirmationMode: options.confirmationMode?.() ?? null,
|
|
404
|
+
attachmentLifecycle: options.settings()?.attachmentLifecycle !== void 0
|
|
405
|
+
})
|
|
398
406
|
})]
|
|
399
407
|
};
|
|
400
408
|
}
|
|
@@ -466,7 +474,7 @@ function gatedRoute(input) {
|
|
|
466
474
|
await Promise.all(pending);
|
|
467
475
|
}
|
|
468
476
|
if (inbound.retryReference !== void 0) return Response.json({
|
|
469
|
-
error: "
|
|
477
|
+
error: "telegram_turn_retry",
|
|
470
478
|
reference: inbound.retryReference
|
|
471
479
|
}, {
|
|
472
480
|
status: 503,
|
|
@@ -11,6 +11,7 @@ export interface ConfirmationCopy {
|
|
|
11
11
|
readonly cancelled: string;
|
|
12
12
|
readonly failed: string;
|
|
13
13
|
readonly expired?: string;
|
|
14
|
+
readonly interrupted?: string;
|
|
14
15
|
readonly edit?: string;
|
|
15
16
|
}
|
|
16
17
|
export interface ConfirmationText {
|
|
@@ -23,6 +24,7 @@ export interface ConfirmationText {
|
|
|
23
24
|
readonly completed: string;
|
|
24
25
|
readonly failed: string;
|
|
25
26
|
readonly expired: string;
|
|
27
|
+
readonly interrupted: string;
|
|
26
28
|
readonly edit: string;
|
|
27
29
|
}
|
|
28
30
|
export declare function confirmationLocale(principal: SessionAuthContext | null, fallback?: string): string;
|
|
@@ -38,7 +38,11 @@ function confirmationCopy(value) {
|
|
|
38
38
|
"cancelled",
|
|
39
39
|
"failed"
|
|
40
40
|
];
|
|
41
|
-
const optional = [
|
|
41
|
+
const optional = [
|
|
42
|
+
"expired",
|
|
43
|
+
"interrupted",
|
|
44
|
+
"edit"
|
|
45
|
+
];
|
|
42
46
|
const valid = (key) => {
|
|
43
47
|
const text = copy[key];
|
|
44
48
|
return typeof text === "string" && text === text.trim() && (key === "question" || !/[✅❌⌛⚠❓✏]/u.test(text)) && humanText(text, key === "question" ? 300 : 24) === text;
|
|
@@ -72,6 +76,7 @@ function renderConfirmation(description, copy) {
|
|
|
72
76
|
completed: copy === void 0 ? "✅" : `✅ ${copy.done}`,
|
|
73
77
|
failed: copy === void 0 ? "⚠️" : `⚠️ ${copy.failed}`,
|
|
74
78
|
expired: copy?.expired === void 0 ? "⌛" : `⌛ ${copy.expired}`,
|
|
79
|
+
interrupted: copy?.interrupted === void 0 ? "⏸" : `⏸ ${copy.interrupted}`,
|
|
75
80
|
edit: copy?.edit === void 0 ? "✏️" : `✏️ ${copy.edit}`
|
|
76
81
|
};
|
|
77
82
|
}
|
|
@@ -70,7 +70,7 @@ function routingConfirmationState(events, prompt, at) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
if (event.type === "input.resolved" && Array.isArray(data.resolutions)) {
|
|
73
|
-
for (const resolution of data.resolutions) if (object(resolution).requestId === prompt.requestId) state = object(resolution).outcome === "approved" ? "approved" : "cancelled";
|
|
73
|
+
for (const resolution of data.resolutions) if (object(resolution).requestId === prompt.requestId) state = object(resolution).outcome === "approved" ? "approved" : object(resolution).outcome === "ignored" ? "interrupted" : "cancelled";
|
|
74
74
|
}
|
|
75
75
|
if (event.type === "action.result" && callId !== void 0 && object(data.result).callId === callId) state = data.status === "completed" ? "completed" : "failed";
|
|
76
76
|
}
|
|
@@ -361,7 +361,7 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
|
|
|
361
361
|
const [callId, message] = entry;
|
|
362
362
|
const copy = message.copy ?? renderConfirmation();
|
|
363
363
|
const reason = confirmations.get()[callId]?.cancellationReason;
|
|
364
|
-
const status = resolution.outcome === "approved" ? copy.executing : reason === "expired" ? copy.expired : copy.cancelled;
|
|
364
|
+
const status = resolution.outcome === "approved" ? copy.executing : reason === "expired" ? copy.expired : resolution.outcome === "ignored" ? copy.interrupted : copy.cancelled;
|
|
365
365
|
const text = message.exactPost ? chatPostCardStatus(message.text, status) : `${message.text}\n\n${status}`;
|
|
366
366
|
const edit = () => channel.telegram.editMessageText({
|
|
367
367
|
messageId: message.messageId,
|
|
@@ -370,7 +370,7 @@ function telegramConfirmationEvents(archive, observed, deferredOptions) {
|
|
|
370
370
|
});
|
|
371
371
|
const prompt = {
|
|
372
372
|
...messagePrompt(message),
|
|
373
|
-
state: resolution.outcome === "approved" ? "approved" : reason === "expired" ? "expired" : "cancelled"
|
|
373
|
+
state: resolution.outcome === "approved" ? "approved" : reason === "expired" ? "expired" : resolution.outcome === "ignored" ? "interrupted" : "cancelled"
|
|
374
374
|
};
|
|
375
375
|
message.prompt = prompt;
|
|
376
376
|
await withConfirmationPrompt(prompt, () => message.exactPost ? withChatPostCard(text, edit, [...message.previewLinks ?? [], ...telegramTextLinks(status)]) : withConfirmationText(text, edit));
|
|
@@ -9,4 +9,8 @@ export declare function healthRoute(input: {
|
|
|
9
9
|
readonly enabled: () => boolean;
|
|
10
10
|
readonly configured: () => boolean;
|
|
11
11
|
readonly verify: () => RuntimeTokenVerifier;
|
|
12
|
+
readonly configuration?: () => {
|
|
13
|
+
readonly confirmationMode: "deferred" | "held" | null;
|
|
14
|
+
readonly attachmentLifecycle: boolean;
|
|
15
|
+
};
|
|
12
16
|
}): RouteDefinition<TelegramChannelState>;
|
|
@@ -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 { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
7
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-CR18wzcH.mjs";
|
|
8
8
|
import { runtimeRequestFailure } from "./runtime-request.mjs";
|
|
9
9
|
import { GET } from "eve/channels";
|
|
10
10
|
const HEALTH_ROUTE = "/eve/v1/telegram/health";
|
|
@@ -16,7 +16,8 @@ function healthRoute(input) {
|
|
|
16
16
|
return Response.json({
|
|
17
17
|
status: registry.degraded ? "degraded" : "ok",
|
|
18
18
|
registry,
|
|
19
|
-
runtime: agentBuild()
|
|
19
|
+
runtime: agentBuild(),
|
|
20
|
+
...input.configuration === void 0 ? {} : { configuration: input.configuration() }
|
|
20
21
|
}, {
|
|
21
22
|
status: registry.degraded ? 503 : 200,
|
|
22
23
|
headers: { "cache-control": "no-store" }
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CancelTurnResult } from "eve/channels";
|
|
2
|
+
import type { RecordedEvent } from "./sessions.js";
|
|
3
|
+
/** An approval is an input barrier, never permission to execute it while recovering a chat. */
|
|
4
|
+
export declare function heldApprovals(events: readonly RecordedEvent[]): {
|
|
5
|
+
requestId: string;
|
|
6
|
+
callId: string;
|
|
7
|
+
action: string;
|
|
8
|
+
turnId: string;
|
|
9
|
+
at: string;
|
|
10
|
+
}[];
|
|
11
|
+
/** Cancel the exact parked turn; never steer a new member into its permissions or old task. */
|
|
12
|
+
export declare function releaseHeldApproval(input: {
|
|
13
|
+
readonly events: readonly RecordedEvent[];
|
|
14
|
+
readonly addressed: boolean;
|
|
15
|
+
readonly now: number;
|
|
16
|
+
readonly cancel: (options: {
|
|
17
|
+
readonly turnId: string;
|
|
18
|
+
}) => Promise<CancelTurnResult>;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
status: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
turnId: string;
|
|
23
|
+
startedAt: string;
|
|
24
|
+
actions: string[];
|
|
25
|
+
cancellation: "accepted";
|
|
26
|
+
executionRequested: boolean;
|
|
27
|
+
} | undefined>;
|
|
@@ -0,0 +1,51 @@
|
|
|
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 { object } from "../confirmation-description.mjs";
|
|
8
|
+
import { CONFIRMATION_TTL_MS } from "../confirmations.mjs";
|
|
9
|
+
function heldApprovals(events) {
|
|
10
|
+
const pending = new Map();
|
|
11
|
+
for (const event of events) {
|
|
12
|
+
const data = object(event.data);
|
|
13
|
+
if (event.type === "context.cleared" || event.type === "session.started") pending.clear();
|
|
14
|
+
if (event.type === "input.requested" && Array.isArray(data.requests)) for (const item of data.requests) {
|
|
15
|
+
const request = object(item);
|
|
16
|
+
const action = object(request.action);
|
|
17
|
+
if (request.kind === "tool-approval" && typeof request.requestId === "string" && typeof action.callId === "string" && typeof data.turnId === "string") pending.set(request.requestId, {
|
|
18
|
+
requestId: request.requestId,
|
|
19
|
+
callId: action.callId,
|
|
20
|
+
action: String(action.toolName ?? ""),
|
|
21
|
+
turnId: data.turnId,
|
|
22
|
+
at: event.meta.at
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
for (const [id, approval] of pending) if (event.type === "input.resolved" && Array.isArray(data.resolutions) && data.resolutions.some((item) => object(item).requestId === id) || event.type === "action.result" && object(data.result).callId === approval.callId || [
|
|
26
|
+
"turn.completed",
|
|
27
|
+
"turn.failed",
|
|
28
|
+
"turn.cancelled"
|
|
29
|
+
].includes(event.type) && data.turnId === approval.turnId) pending.delete(id);
|
|
30
|
+
}
|
|
31
|
+
return [...pending.values()];
|
|
32
|
+
}
|
|
33
|
+
async function releaseHeldApproval(input) {
|
|
34
|
+
const pending = heldApprovals(input.events);
|
|
35
|
+
if (new Set(pending.map((approval) => approval.turnId)).size !== 1) return;
|
|
36
|
+
const expired = pending.some((approval) => input.now - Date.parse(approval.at) >= CONFIRMATION_TTL_MS);
|
|
37
|
+
if (!input.addressed && !expired) return;
|
|
38
|
+
const turnId = pending[0].turnId;
|
|
39
|
+
const result = await input.cancel({ turnId });
|
|
40
|
+
if (result.status === "no_active_turn") return;
|
|
41
|
+
return {
|
|
42
|
+
status: "cancellation_requested",
|
|
43
|
+
reason: expired ? "expired" : "addressed",
|
|
44
|
+
turnId,
|
|
45
|
+
startedAt: pending[0].at,
|
|
46
|
+
actions: pending.map((approval) => approval.action),
|
|
47
|
+
cancellation: result.status,
|
|
48
|
+
executionRequested: false
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export { heldApprovals, releaseHeldApproval };
|
|
@@ -37,7 +37,7 @@ export declare class InboundRequest {
|
|
|
37
37
|
stopTyping: (() => void) | undefined;
|
|
38
38
|
lifecycle: ReturnType<typeof import("./lifecycle.js").attachmentLifecycle> | undefined;
|
|
39
39
|
lifecycleKey: string | undefined;
|
|
40
|
-
/**
|
|
40
|
+
/** A routed task must be redelivered if recovery or intake did not reach durable admission. */
|
|
41
41
|
retryReference: string | undefined;
|
|
42
42
|
lifecycleContext: string | undefined;
|
|
43
43
|
monitor: ((session: Awaited<ReturnType<ChannelSource<unknown>["send"]>>, marker: string) => void) | undefined;
|
|
@@ -39,6 +39,8 @@ import type { PdfTextExtractor } from "./pdf-text.js";
|
|
|
39
39
|
*/
|
|
40
40
|
export interface InboundDependencies {
|
|
41
41
|
readonly policy: TelegramPolicy;
|
|
42
|
+
/** Recover old held sessions only when the workspace has chosen non-blocking confirmations. */
|
|
43
|
+
readonly recoverHeldApprovals?: boolean;
|
|
42
44
|
readonly lifecycle?: (principal: import("eve/context").SessionAuthContext, memberId: string, message: TelegramMessage) => ReturnType<typeof attachmentLifecycle>;
|
|
43
45
|
readonly admission: () => AdmissionSettings;
|
|
44
46
|
readonly sessions: () => SessionSettings;
|
|
@@ -22,6 +22,7 @@ import { recordGateDecision, recordInboundContent, recordRouteDecision } from ".
|
|
|
22
22
|
import { chatContext } from "./chats.mjs";
|
|
23
23
|
import { admitMessage, localizedText } from "../admission.mjs";
|
|
24
24
|
import { attachmentsContext, inboundAttachments, stageFiles } from "./files.mjs";
|
|
25
|
+
import { heldApprovals, releaseHeldApproval } from "./held-approvals.mjs";
|
|
25
26
|
import { voiceOf, voiceText } from "./voice.mjs";
|
|
26
27
|
const UNKNOWN_PRIVATE = "🔗";
|
|
27
28
|
const UNKNOWN_IN_GROUP = "🔒";
|
|
@@ -367,7 +368,8 @@ async function handleInboundMessage(input) {
|
|
|
367
368
|
if (input.request !== void 0 && sent.attachments.length > 0 && dependencies.lifecycle !== void 0 && await admitTurn(true)) {
|
|
368
369
|
const principal = telegramPrincipal({
|
|
369
370
|
message: sent,
|
|
370
|
-
member
|
|
371
|
+
member,
|
|
372
|
+
conversationLanguage: conversationLanguage([sent.text || sent.caption, ...dependencies.observed.recent(chat).map((message) => message.text).reverse()])
|
|
371
373
|
});
|
|
372
374
|
input.request.lifecycle = dependencies.lifecycle(principal, member.id, sent);
|
|
373
375
|
input.request.stopTyping = startTypingHeartbeat(context.telegram);
|
|
@@ -721,6 +723,38 @@ async function handleInboundMessage(input) {
|
|
|
721
723
|
recordGateDecision("message_already_handled");
|
|
722
724
|
return null;
|
|
723
725
|
}
|
|
726
|
+
if (dependencies.recoverHeldApprovals === true && operations !== void 0 && dependencies.history !== void 0) {
|
|
727
|
+
let recoverySessionId;
|
|
728
|
+
try {
|
|
729
|
+
const session = await operations.resolveSession(address);
|
|
730
|
+
if (session !== void 0) {
|
|
731
|
+
recoverySessionId = session.id;
|
|
732
|
+
const previous = session.id === owner?.id ? questionHistory : await dependencies.history(session.id);
|
|
733
|
+
const recovery = heldApprovals(previous).length === 0 ? void 0 : await releaseHeldApproval({
|
|
734
|
+
events: await dependencies.history(session.id),
|
|
735
|
+
addressed: addressed !== void 0,
|
|
736
|
+
now: Date.now(),
|
|
737
|
+
cancel: (options) => {
|
|
738
|
+
if (session.cancel === void 0) throw new Error("The parked session does not support turn cancellation.");
|
|
739
|
+
return session.cancel(options);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
if (recovery !== void 0) {
|
|
743
|
+
recordGateDecision("held_approval_cancellation_requested", "reply");
|
|
744
|
+
notes.push(`<approval_recovery>${JSON.stringify(recovery)}</approval_recovery>`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
} catch (error) {
|
|
748
|
+
if (input.request !== void 0) {
|
|
749
|
+
input.request.retryReference = reportTelegramError("held_approval_release_failed", error, {
|
|
750
|
+
sessionId: recoverySessionId,
|
|
751
|
+
messageId: sent.messageId
|
|
752
|
+
});
|
|
753
|
+
await input.request.finishClaim?.(false);
|
|
754
|
+
}
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
724
758
|
if (voice !== void 0 && input.request !== void 0) input.request.text = text;
|
|
725
759
|
else if (voice !== void 0) notes.push(text);
|
|
726
760
|
if (answering && pending !== void 0 && input.request !== void 0) {
|
|
@@ -6,6 +6,8 @@ import type { RuntimeToken } from "../runtime-token.js";
|
|
|
6
6
|
export interface AttachmentLifecycle {
|
|
7
7
|
readonly entity: string;
|
|
8
8
|
readonly action: string;
|
|
9
|
+
/** Opt into provider timestamps and completion replies in lifecycle data. Omit for the legacy data contract. */
|
|
10
|
+
readonly contextVersion?: 2;
|
|
9
11
|
}
|
|
10
12
|
export interface LifecycleResult {
|
|
11
13
|
readonly key?: string;
|
|
@@ -35,6 +37,7 @@ export declare function attachmentLifecycle(options: {
|
|
|
35
37
|
readonly from?: {
|
|
36
38
|
readonly id: string;
|
|
37
39
|
};
|
|
40
|
+
readonly raw?: Record<string, unknown>;
|
|
38
41
|
};
|
|
39
42
|
readonly environment?: Environment;
|
|
40
43
|
readonly token?: RuntimeToken;
|
|
@@ -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 { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
7
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-CR18wzcH.mjs";
|
|
8
8
|
import { object } from "../confirmation-description.mjs";
|
|
9
9
|
import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-B98tr4Pd.mjs";
|
|
10
10
|
import { needsConfirmation } from "../confirmations.mjs";
|
|
@@ -32,7 +32,10 @@ function attachmentLifecycle(options) {
|
|
|
32
32
|
sender: options.message.from.id,
|
|
33
33
|
locale: String(options.principal.attributes.conversationLanguage ?? options.principal.attributes.locale ?? "en")
|
|
34
34
|
},
|
|
35
|
-
data
|
|
35
|
+
data: {
|
|
36
|
+
...data,
|
|
37
|
+
...options.configuration.contextVersion === 2 && typeof options.message.raw?.date === "number" && Number.isSafeInteger(options.message.raw.date) && options.message.raw.date > 0 ? { source_at: options.message.raw.date * 1e3 } : {}
|
|
38
|
+
}
|
|
36
39
|
};
|
|
37
40
|
if (needsConfirmation(await tools.labels(options.memberId, action, input))) throw new Error("An attachment lifecycle hook must be a permitted routine action.");
|
|
38
41
|
const raw = await tools.rpc(options.memberId, "tools/call", {
|
|
@@ -62,7 +62,7 @@ export interface AgentMessage extends ObservedMessage {
|
|
|
62
62
|
}
|
|
63
63
|
/** Presentation only; no tool inputs, credential values or member identifiers. */
|
|
64
64
|
export interface PendingConfirmation {
|
|
65
|
-
readonly state?: "pending" | "approved" | "cancelled" | "expired" | "completed" | "failed";
|
|
65
|
+
readonly state?: "pending" | "approved" | "cancelled" | "interrupted" | "expired" | "completed" | "failed";
|
|
66
66
|
readonly action: string;
|
|
67
67
|
readonly requestedBy: string | null;
|
|
68
68
|
readonly at: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TelegramMessage } from "eve/channels/telegram";
|
|
2
|
+
import type { CancelTurnResult } from "eve/channels";
|
|
2
3
|
import type { SessionSettings } from "../../config.js";
|
|
3
4
|
/**
|
|
4
5
|
* Session policy for the Telegram channel (`agent-design.md`, section 4, and docs/agent.md,
|
|
@@ -56,6 +57,9 @@ export declare function historyOf(events: ReadonlyArray<RecordedEvent>): Session
|
|
|
56
57
|
export interface SessionOperations {
|
|
57
58
|
readonly resolveSession: (address: string) => Promise<{
|
|
58
59
|
readonly id: string;
|
|
60
|
+
readonly cancel?: (options: {
|
|
61
|
+
readonly turnId: string;
|
|
62
|
+
}) => Promise<CancelTurnResult>;
|
|
59
63
|
} | undefined>;
|
|
60
64
|
readonly from: (address: string) => {
|
|
61
65
|
compact(): Promise<unknown>;
|
|
@@ -24,11 +24,20 @@ function replyDelivery(observed, scope = () => "telegram") {
|
|
|
24
24
|
return async (event, channel, context) => {
|
|
25
25
|
if (completionKind(event.message, event.finishReason) !== "reply") return;
|
|
26
26
|
const message = customerMessage(event.message, confirmationLocale(context.session?.auth.current ?? null, currentLocale()));
|
|
27
|
+
const principal = context.session?.auth.current;
|
|
28
|
+
const replyTo = principal?.attributes.replyToMessageId;
|
|
29
|
+
const replyParameters = typeof replyTo === "string" && /^[1-9]\d*$/u.test(replyTo) ? { reply_parameters: {
|
|
30
|
+
message_id: Number(replyTo),
|
|
31
|
+
allow_sending_without_reply: true
|
|
32
|
+
} } : {};
|
|
27
33
|
const deliveredIds = await inTelegramSendQueue(scope(), channel.telegram.chatId, async () => {
|
|
28
34
|
const messageIds = [];
|
|
29
35
|
try {
|
|
30
36
|
for (const part of splitTelegramText(message)) {
|
|
31
|
-
const result = await channel.telegram.post(
|
|
37
|
+
const result = await channel.telegram.post({
|
|
38
|
+
...part,
|
|
39
|
+
...replyParameters
|
|
40
|
+
});
|
|
32
41
|
if (!/^[1-9]\d*$/u.test(result.id)) throw new Error("Telegram did not acknowledge the reply with a message id.");
|
|
33
42
|
messageIds.push(Number(result.id));
|
|
34
43
|
}
|
|
@@ -40,7 +49,6 @@ function replyDelivery(observed, scope = () => "telegram") {
|
|
|
40
49
|
});
|
|
41
50
|
const text = message.text;
|
|
42
51
|
if (observed === void 0 || channel.telegram.chatType === "private") return;
|
|
43
|
-
const principal = context.session.auth.current;
|
|
44
52
|
const sender = principal?.authenticator === "segmentstream-telegram" ? principal.subject : void 0;
|
|
45
53
|
const username = principal?.attributes.username;
|
|
46
54
|
const name = principal?.attributes.memberName;
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
declare const _default: import("eve").DynamicSentinel<{
|
|
2
|
-
|
|
2
|
+
ontology_discover: import("eve/tools").ToolDefinition<Record<string, unknown>, Record<string, unknown>> & {
|
|
3
|
+
execute(input: Record<string, unknown>, ctx: import("eve/tools").ToolContext): Promise<Record<string, unknown>>;
|
|
4
|
+
};
|
|
5
|
+
ontology_describe: import("eve/tools").ToolDefinition<Record<string, unknown>, Record<string, unknown>> & {
|
|
6
|
+
execute(input: Record<string, unknown>, ctx: import("eve/tools").ToolContext): Promise<Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
ontology_inspect: import("eve/tools").ToolDefinition<Record<string, unknown>, {
|
|
9
|
+
next_after?: string | undefined;
|
|
10
|
+
tools: {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
}[];
|
|
14
|
+
}> & {
|
|
15
|
+
execute(input: Record<string, unknown>, ctx: import("eve/tools").ToolContext): Promise<{
|
|
16
|
+
next_after?: string | undefined;
|
|
17
|
+
tools: {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
ontology_call: import("eve/tools").ToolDefinition<Record<string, unknown>, Record<string, unknown>> & {
|
|
24
|
+
execute(input: Record<string, unknown>, ctx: import("eve/tools").ToolContext): Promise<Record<string, unknown>>;
|
|
25
|
+
};
|
|
3
26
|
} | null>;
|
|
4
27
|
export default _default;
|
|
@@ -6,25 +6,125 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { memberIdOf } from "../lib/telegram/principal.mjs";
|
|
8
8
|
import segmentstream from "../extension.mjs";
|
|
9
|
-
import { ontologyTools } from "../lib/ontology-tools.mjs";
|
|
10
9
|
import { executeDeferredTool } from "../lib/deferred-confirmations.mjs";
|
|
10
|
+
import { ontologyInspect, ontologyMetadata } from "../lib/ontology-gateway.mjs";
|
|
11
11
|
import { defineDynamic, defineTool } from "eve/tools";
|
|
12
12
|
var ontology_default = defineDynamic({ events: { "turn.started": async (_event, context) => {
|
|
13
13
|
const memberId = memberIdOf(context.session.auth.current);
|
|
14
14
|
if (!segmentstream.config.ontology.connection || segmentstream.config.confirmations.mode !== "deferred" || memberId === void 0) return null;
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const properties = Object.fromEntries(Object.entries(schema.properties ?? {}).filter(([key]) => key !== "_segmentstream"));
|
|
19
|
-
return [`ontology__${action}`, defineTool({
|
|
20
|
-
deferred: true,
|
|
21
|
-
description: String(tool.description ?? tool.title ?? action),
|
|
15
|
+
return {
|
|
16
|
+
ontology_discover: defineTool({
|
|
17
|
+
description: "Search the permitted ontology's entities, actions and measures by words. Inspect returned refs with ontology_describe before calling ontology_call.",
|
|
22
18
|
inputSchema: {
|
|
23
|
-
|
|
24
|
-
properties
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
query: {
|
|
22
|
+
type: "string",
|
|
23
|
+
minLength: 1,
|
|
24
|
+
maxLength: 512
|
|
25
|
+
},
|
|
26
|
+
kind: {
|
|
27
|
+
type: "string",
|
|
28
|
+
enum: [
|
|
29
|
+
"entity",
|
|
30
|
+
"action",
|
|
31
|
+
"measure"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
entity: { type: "string" },
|
|
35
|
+
mode: {
|
|
36
|
+
type: "string",
|
|
37
|
+
enum: [
|
|
38
|
+
"auto",
|
|
39
|
+
"literal",
|
|
40
|
+
"fuzzy"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
after: { type: "string" },
|
|
44
|
+
expected_hash: {
|
|
45
|
+
type: "string",
|
|
46
|
+
pattern: "^sha256:[a-f0-9]{64}$"
|
|
47
|
+
},
|
|
48
|
+
limit: {
|
|
49
|
+
type: "integer",
|
|
50
|
+
minimum: 1,
|
|
51
|
+
maximum: 50
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
required: ["query"],
|
|
55
|
+
additionalProperties: false
|
|
25
56
|
},
|
|
26
|
-
execute: (input, ctx) =>
|
|
27
|
-
})
|
|
28
|
-
|
|
57
|
+
execute: (input, ctx) => ontologyMetadata(memberId, "discover", input, ctx)
|
|
58
|
+
}),
|
|
59
|
+
ontology_describe: defineTool({
|
|
60
|
+
description: "Inspect an ontology entity:, action: or measure: ref, including its exact operation names and schemas. Omit ref for the entity index. Follow next_cursor with after.",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
ref: {
|
|
65
|
+
type: "string",
|
|
66
|
+
minLength: 1,
|
|
67
|
+
maxLength: 256
|
|
68
|
+
},
|
|
69
|
+
after: { type: "string" },
|
|
70
|
+
expected_hash: {
|
|
71
|
+
type: "string",
|
|
72
|
+
pattern: "^sha256:[a-f0-9]{64}$"
|
|
73
|
+
},
|
|
74
|
+
limit: {
|
|
75
|
+
type: "integer",
|
|
76
|
+
minimum: 1,
|
|
77
|
+
maximum: 100
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
additionalProperties: false
|
|
81
|
+
},
|
|
82
|
+
execute: (input, ctx) => ontologyMetadata(memberId, "describe", input, ctx)
|
|
83
|
+
}),
|
|
84
|
+
ontology_inspect: defineTool({
|
|
85
|
+
description: "Find permission-filtered MCP operation names and complete input schemas, including entity find/get/history reads. Supply name for an exact definition, query for words, or omit both for a paged tool index. Use next_after as after. This reads metadata only.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {
|
|
89
|
+
name: {
|
|
90
|
+
type: "string",
|
|
91
|
+
minLength: 1
|
|
92
|
+
},
|
|
93
|
+
query: {
|
|
94
|
+
type: "string",
|
|
95
|
+
minLength: 1,
|
|
96
|
+
maxLength: 512
|
|
97
|
+
},
|
|
98
|
+
after: { type: "string" },
|
|
99
|
+
limit: {
|
|
100
|
+
type: "integer",
|
|
101
|
+
minimum: 1,
|
|
102
|
+
maximum: 50
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
additionalProperties: false
|
|
106
|
+
},
|
|
107
|
+
execute: (input, ctx) => ontologyInspect(memberId, input, ctx)
|
|
108
|
+
}),
|
|
109
|
+
ontology_call: defineTool({
|
|
110
|
+
description: "Run a permitted ontology operation by the exact MCP tool name from its inspected definition, with arguments matching that schema. Reads and routine actions run immediately; consequential actions retain native deferred confirmation. An awaiting_confirmation result means the action has not run.",
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: "object",
|
|
113
|
+
properties: {
|
|
114
|
+
name: {
|
|
115
|
+
type: "string",
|
|
116
|
+
minLength: 1
|
|
117
|
+
},
|
|
118
|
+
arguments: {
|
|
119
|
+
type: "object",
|
|
120
|
+
additionalProperties: true
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
required: ["name"],
|
|
124
|
+
additionalProperties: false
|
|
125
|
+
},
|
|
126
|
+
execute: (input, ctx) => executeDeferredTool(memberId, input.name, input.arguments ?? {}, ctx)
|
|
127
|
+
})
|
|
128
|
+
};
|
|
29
129
|
} } });
|
|
30
130
|
export { ontology_default as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream/agent",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "Mount a SegmentStream workspace's agent on eve: the ontology as tools, acting as the member who writes, and Telegram through the workspace's connection.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@chat-adapter/telegram": "4.41.0",
|
|
65
|
-
"@segmentstream-cloud/ontology": "0.
|
|
65
|
+
"@segmentstream-cloud/ontology": "0.35.0",
|
|
66
66
|
"@vercel/oidc": "^3.8.4",
|
|
67
67
|
"ai": "^7.0.128",
|
|
68
68
|
"csv-parse": "^7.0.3",
|