@segmentstream/agent 0.10.4 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -451,7 +451,7 @@ To reach a local ontology service, set `SEGMENTSTREAM_AGENT_ONTOLOGY_URL=http://
451
451
  production tokens of the workspace's project, though, so a local run gets the ontology only from a
452
452
  local ontology service configured to accept that token.
453
453
 
454
- Building the package needs Node.js 24, as eve does: `npm run build` runs `eve extension build`,
454
+ Building the package needs Node.js 24, as eve does: `pnpm run build` runs `eve extension build`,
455
455
  which writes the agent-shaped `dist/extension` tree, its declarations and eve's compatibility
456
456
  manifest.
457
457
 
@@ -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.10.4";
7
+ var version = "0.12.0";
8
8
  function agentBuild(environment = process.env) {
9
9
  return {
10
10
  agent: version,
@@ -20,7 +20,6 @@ export { chooseModel, model, type DynamicModel, type ModelChoice } from "./model
20
20
  export { groupPolicy, effectiveGroupPolicy, DEFAULT_GROUP_POLICY, type GroupPolicy, type GroupPolicyInput, type SmartPolicyInput, type ChatOverrideInput, } from "./lib/group-routing/policy.js";
21
21
  export { addressingReason as groupAddressingReason, conversationKey, type GroupMessageSignals, type AgentMessageSignals, type GroupChannelAdapter, } from "./lib/group-routing/signals.js";
22
22
  export { routeMessage as routeGroupMessage, decisionRouter as groupDecisionRouter, } from "./lib/group-routing/router.js";
23
- export { groupSettings as readGroupSettings, type CloudGroupSettings, } from "./lib/group-routing/settings.js";
24
23
  export { observedMessages as groupObservedMessages, agentMessage as groupAgentMessage, type RecentMessage, type AgentMessage, type ObservedMessage, } from "./lib/group-routing/observed.js";
25
24
  export { archivedRoutingMessages } from "./lib/group-routing/routing-context.js";
26
25
  export { replayRouterPayload } from "./lib/group-routing/replay.js";
@@ -10,7 +10,6 @@ import { chooseModel, model } from "./model.mjs";
10
10
  import { addressingReason, conversationKey } from "./lib/group-routing/signals.mjs";
11
11
  import { conversationAddressing, conversationState } from "./lib/group-routing/conversation.mjs";
12
12
  import { decisionRouter, routeMessage } from "./lib/group-routing/router.mjs";
13
- import { groupSettings } from "./lib/group-routing/settings.mjs";
14
13
  import { agentMessage, observedMessages } from "./lib/group-routing/observed.mjs";
15
14
  import { archivedRoutingMessages } from "./lib/group-routing/routing-context.mjs";
16
15
  import { replayRouterPayload } from "./lib/group-routing/replay.mjs";
@@ -21,4 +20,4 @@ const segmentstream = Object.assign(extension, {
21
20
  model: (choice) => model(choice ?? (() => extension.config.models.chat)),
22
21
  manifestOf
23
22
  });
24
- export { DEFAULT_ADMISSION_LIMITS, DEFAULT_DECISION_MODEL, DEFAULT_GROUP_POLICY, DEFAULT_STT_MODEL, SegmentStreamAgentConfigError, archivedRoutingMessages, chooseModel, conversationKey, debounceBurst, segmentstream as default, segmentstream, effectiveGroupPolicy, addressingReason as groupAddressingReason, agentMessage as groupAgentMessage, conversationAddressing as groupConversationAddressing, conversationState as groupConversationState, decisionRouter as groupDecisionRouter, observedMessages as groupObservedMessages, groupPolicy, manifestOf, model, groupSettings as readGroupSettings, replayRouterPayload, routeMessage as routeGroupMessage, routingBurst };
23
+ export { DEFAULT_ADMISSION_LIMITS, DEFAULT_DECISION_MODEL, DEFAULT_GROUP_POLICY, DEFAULT_STT_MODEL, SegmentStreamAgentConfigError, archivedRoutingMessages, chooseModel, conversationKey, debounceBurst, segmentstream as default, segmentstream, effectiveGroupPolicy, addressingReason as groupAddressingReason, agentMessage as groupAgentMessage, conversationAddressing as groupConversationAddressing, conversationState as groupConversationState, decisionRouter as groupDecisionRouter, observedMessages as groupObservedMessages, groupPolicy, manifestOf, model, replayRouterPayload, routeMessage as routeGroupMessage, routingBurst };
@@ -19,6 +19,16 @@ talking to you, and you act with exactly their permissions.
19
19
  reload definitions already known for the current ontology hash. Metadata visibility does
20
20
  not grant record access. The operation tools are narrowed to the member's current permissions;
21
21
  if an operation is missing, say so instead of looking for a way around.
22
+ - When the ontology offers `app_feedback_submit`, the app accepts feedback for its builders.
23
+ Offer to send it when the member wants something no permitted tool, field or data provides, an
24
+ operation is refused for lack of permission, an operation fails unexpectedly or a retryable
25
+ failure persists, or the member says something works badly or asks for something new. Do not
26
+ offer what you can do yourself; offer once per problem. Before submitting, look for the member's
27
+ open `app_feedback` with the same subject. Submit only after the member agrees to the title and
28
+ summary you show in their language, then add each screenshot or file they sent with
29
+ `app_feedback_file_attach`, promoting a chat attachment into it. When a member starts talking to
30
+ you, find their `app_feedback` with `responded_at` set and `seen_at` empty, tell them the
31
+ builders' response, then call `app_feedback_acknowledge` with its `responded_at`.
22
32
  Older ontologies without `discover` return the legacy full catalog from `describe`; use that
23
33
  available contract. New description results carry `kind` and `hash`.
24
34
  - When the current member has permission and gives a clear instruction, act, including necessary
@@ -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-CFAnvrjf.mjs";
7
+ import { t as agentBuild } from "../../_chunks/build-metadata-SU1WsppW.mjs";
8
8
  export { agentBuild };
@@ -23,9 +23,6 @@ export interface SmartPolicy {
23
23
  readonly context: {
24
24
  readonly messages: number;
25
25
  readonly includeAgentDescription: boolean;
26
- readonly includeAgentInstructions: boolean | {
27
- readonly maxChars: number;
28
- };
29
26
  readonly includeChatGuidance: boolean;
30
27
  readonly includeParticipants: boolean;
31
28
  };
@@ -77,5 +74,5 @@ export declare function groupPolicy(input?: GroupPolicyInput, aliases?: {
77
74
  /** Dynamic defaults change only when inherited from the platform; authored fields retain precedence. */
78
75
  export declare function overlaySmartPolicy(input: SmartPolicyInput | undefined, base: GroupPolicy): SmartPolicy;
79
76
  export declare const DEFAULT_GROUP_POLICY: GroupPolicy;
80
- /** Agent -> Cloud override -> pinned chat -> registry. Provider-specific switches only map signals. */
81
- export declare function effectiveGroupPolicy(base: GroupPolicy, key: string, registry?: ChatOverrideInput, cloud?: GroupPolicyInput): GroupPolicy;
77
+ /** Agent -> chat -> chat scope, as `groups` authors them. Provider switches only map signals. */
78
+ export declare function effectiveGroupPolicy(base: GroupPolicy, key: string): GroupPolicy;
@@ -40,7 +40,6 @@ function smartPolicy(input = {}, base) {
40
40
  context: {
41
41
  messages: conversation ? 60 : 30,
42
42
  includeAgentDescription: true,
43
- includeAgentInstructions: false,
44
43
  includeChatGuidance: true,
45
44
  includeParticipants: true,
46
45
  ...base?.context,
@@ -157,30 +156,10 @@ function overlaySmartPolicy(input, base) {
157
156
  });
158
157
  }
159
158
  const DEFAULT_GROUP_POLICY = groupPolicy();
160
- function effectiveGroupPolicy(base, key, registry, cloud) {
161
- let result = cloud === void 0 ? base : {
162
- ...base,
163
- mode: cloud.mode ?? base.mode,
164
- smart: overlaySmartPolicy(cloud.smart, base),
165
- chats: {
166
- ...base.chats,
167
- ...cloud.chats
168
- },
169
- observability: {
170
- ...base.observability,
171
- ...cloud.observability
172
- },
173
- sources: {
174
- ...base.sources,
175
- ...sources(cloud, "cloud")
176
- }
177
- };
159
+ function effectiveGroupPolicy(base, key) {
160
+ let result = base;
178
161
  const parent = key.includes("/") ? result.chats[key.slice(0, key.indexOf("/"))] : void 0;
179
- for (const layer of [
180
- parent,
181
- result.chats[key],
182
- registry
183
- ]) if (layer !== void 0) result = {
162
+ for (const layer of [parent, result.chats[key]]) if (layer !== void 0) result = {
184
163
  ...result,
185
164
  mode: layer.mode ?? result.mode,
186
165
  smart: overlaySmartPolicy(layer.smart, result),
@@ -190,7 +169,7 @@ function effectiveGroupPolicy(base, key, registry, cloud) {
190
169
  },
191
170
  sources: {
192
171
  ...result.sources,
193
- ...sources(layer, layer === registry ? "registry" : "agent.chat")
172
+ ...sources(layer, "agent.chat")
194
173
  }
195
174
  };
196
175
  const thresholds = result.smart.thresholds;
@@ -14,7 +14,6 @@ export interface RouteQuestion {
14
14
  readonly processingAt?: string;
15
15
  readonly messagesToClassify?: ReadonlyArray<ObservedMessage>;
16
16
  readonly signals?: GroupMessageSignals;
17
- readonly agentInstructions?: string;
18
17
  /** The agent's own description, so the model knows what it can be asked for. */
19
18
  readonly agent: string;
20
19
  readonly agentIdentity?: Partial<BotIdentity> & {
@@ -121,7 +121,6 @@ function conversationContext(question) {
121
121
  }));
122
122
  return {
123
123
  agent: policy.smart.context.includeAgentDescription ? question.agent.slice(0, 4e3) : "",
124
- ...policy.smart.context.includeAgentInstructions ? { agent_instructions: (question.agentInstructions ?? "").slice(0, typeof policy.smart.context.includeAgentInstructions === "object" ? policy.smart.context.includeAgentInstructions.maxChars : 6e3) } : {},
125
124
  agent_identity: JSON.stringify({
126
125
  display_name: bot?.displayName ?? null,
127
126
  username: bot?.username ?? null,
@@ -1,4 +1,3 @@
1
- import { type ChatOverrideInput } from "@segmentstream-cloud/ontology/groups";
2
1
  import type { AdmissionLimitsInput } from "../config.js";
3
2
  import type { RuntimeToken } from "./runtime-token.js";
4
3
  /**
@@ -69,8 +68,6 @@ export interface ChatAccount {
69
68
  }
70
69
  /** A group chat in the ontology's `agent_chat` registry, with what members said it is for. */
71
70
  export interface AgentChat {
72
- readonly routing?: ChatOverrideInput;
73
- readonly scope?: string;
74
71
  readonly id: string;
75
72
  readonly title: string;
76
73
  readonly purpose: string | null;
@@ -5,7 +5,6 @@ const __filename = __eveFileURLToPath(import.meta.url);
5
5
  __eveDirname(__filename);
6
6
  __eveCreateRequire(import.meta.url);
7
7
  import { redactDiagnostic } from "./diagnostics.mjs";
8
- import { groupPolicyIssues } from "@segmentstream-cloud/ontology/groups";
9
8
  var AgentIdentityError = class extends Error {
10
9
  name = "AgentIdentityError";
11
10
  code;
@@ -94,22 +93,11 @@ function agentChat(value) {
94
93
  code: "invalid_response",
95
94
  message: "The ontology service answered the chat report with an invalid body."
96
95
  });
97
- const override = {
98
- ...isRecord(value.routing) ? value.routing : {},
99
- ...isRecord(value.participants) ? { participants: value.participants } : {}
100
- };
101
- const valid = groupPolicyIssues(override, true).length === 0;
102
- if (!valid) console.error("[segmentstream] invalid chat routing override", {
103
- chatId: value.id,
104
- issues: groupPolicyIssues(override, true)
105
- });
106
96
  return {
107
97
  id: value.id,
108
98
  title: value.title,
109
99
  purpose: value.purpose,
110
- whenToUse: value.whenToUse,
111
- ...typeof value.scope === "string" ? { scope: value.scope } : {},
112
- ...valid && Object.keys(override).length > 0 ? { routing: override } : {}
100
+ whenToUse: value.whenToUse
113
101
  };
114
102
  }
115
103
  async function refusal(response, call) {
@@ -11,10 +11,9 @@ import { chooseModel } from "../../model.mjs";
11
11
  import { groupRoutingHealth } from "../group-routing/health.mjs";
12
12
  import { decisionRouter } from "../group-routing/router.mjs";
13
13
  import { runtimeToken } from "../runtime-token.mjs";
14
- import { groupSettings } from "../group-routing/settings.mjs";
15
14
  import { conversations } from "../conversations.mjs";
16
15
  import { archivedRoutingMessages } from "../group-routing/routing-context.mjs";
17
- import { t as agentBuild } from "../../../_chunks/build-metadata-CFAnvrjf.mjs";
16
+ import { t as agentBuild } from "../../../_chunks/build-metadata-SU1WsppW.mjs";
18
17
  import { confirmationLocale } from "./confirmation-text.mjs";
19
18
  import { telegramSendScope } from "./send-queue.mjs";
20
19
  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-BrWspFw5.mjs";
@@ -71,11 +70,6 @@ const eveInteractions = async (sessionId) => {
71
70
  function segmentstreamTelegram(options) {
72
71
  const environment = options.environment ?? process.env;
73
72
  const token = options.token ?? runtimeToken;
74
- const readGroups = groupSettings({
75
- environment,
76
- token,
77
- fetch: options.fetch
78
- });
79
73
  const observed = options.observed ?? observedMessages(60);
80
74
  const readHistory = options.history ?? eveHistory;
81
75
  const readInteractions = options.history ?? eveInteractions;
@@ -183,7 +177,6 @@ function segmentstreamTelegram(options) {
183
177
  limit: 200,
184
178
  includeDecisions: true
185
179
  })).events, 100),
186
- cloudGroups: options.groups === void 0 ? void 0 : readGroups,
187
180
  signalSettings: settings().groups,
188
181
  recoverHeldApprovals: options.confirmationMode?.() === "deferred",
189
182
  ...settings().attachmentLifecycle === void 0 ? {} : { lifecycle: (principal, memberId, message) => attachmentLifecycle({
@@ -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-CFAnvrjf.mjs";
7
+ import { t as agentBuild } from "../../../_chunks/build-metadata-SU1WsppW.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";
@@ -1,5 +1,4 @@
1
1
  import type { RecentMessage } from "../group-routing/observed.js";
2
- import type { CloudGroupSettings } from "../group-routing/settings.js";
3
2
  import { type GroupPolicy } from "../group-routing/policy.js";
4
3
  import type { TelegramSettings } from "../../config.js";
5
4
  import { attachmentLifecycle } from "./lifecycle.js";
@@ -46,7 +45,6 @@ export interface InboundDependencies {
46
45
  readonly decisionModel?: string;
47
46
  readonly groups?: GroupPolicy;
48
47
  readonly recentConversation?: (chatId: string, threadId?: string) => Promise<ReadonlyArray<RecentMessage>>;
49
- readonly cloudGroups?: () => Promise<CloudGroupSettings | undefined>;
50
48
  readonly signalSettings?: TelegramSettings["groups"];
51
49
  /** Recover old held sessions only when the workspace has chosen non-blocking confirmations. */
52
50
  readonly recoverHeldApprovals?: boolean;
@@ -461,12 +461,10 @@ async function handleInboundMessage(input) {
461
461
  const questionHistory = owner === void 0 || dependencies.history === void 0 ? [] : await dependencies.history(owner.id);
462
462
  await report;
463
463
  const policySignals = telegramSignals(sent, bot, dependencies.signalSettings);
464
- const cloudGroups = await dependencies.cloudGroups?.();
465
- const registryChat = dependencies.chats.chat(sent.chat.id);
466
464
  const groups = effectiveGroupPolicy(dependencies.groups ?? groupPolicy({}, {
467
465
  mode: dependencies.policy,
468
466
  model: dependencies.decisionModel
469
- }), conversationKey(policySignals), registryChat?.scope !== void 0 && registryChat.scope !== policySignals.conversation.scope ? void 0 : registryChat?.routing, cloudGroups?.settings);
467
+ }), conversationKey(policySignals));
470
468
  const conversationPolicy = group && groups.smart.engagement.policy === "conversation";
471
469
  const agentAliases = [...groups.smart.engagement.agentAliases, ...[
472
470
  bot?.username,
@@ -588,7 +586,6 @@ async function handleInboundMessage(input) {
588
586
  }
589
587
  },
590
588
  agent: await input.request?.agentDescription() ?? "",
591
- agentInstructions: cloudGroups?.instructions,
592
589
  agentIdentity: {
593
590
  ...bot,
594
591
  displayName: bot?.displayName ?? name,
@@ -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-CFAnvrjf.mjs";
7
+ import { t as agentBuild } from "../../../_chunks/build-metadata-SU1WsppW.mjs";
8
8
  import { object } from "../confirmation-description.mjs";
9
9
  import { n as currentInboundRequest, p as reportTelegramError } from "../../../_chunks/inbound-request-BrWspFw5.mjs";
10
10
  import { needsConfirmation } from "../confirmations.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@segmentstream/agent",
3
- "version": "0.10.4",
3
+ "version": "0.12.0",
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",
@@ -57,12 +57,12 @@
57
57
  "typecheck": "tsc -p tsconfig.json --noEmit",
58
58
  "lint": "eslint src tests --config ../../eslint.config.mjs",
59
59
  "test": "vitest run",
60
- "prepack": "npm run build",
60
+ "prepack": "pnpm run build",
61
61
  "eval:routing": "tsx tests/routing-eval/cli.ts"
62
62
  },
63
63
  "dependencies": {
64
64
  "@chat-adapter/telegram": "4.41.0",
65
- "@segmentstream-cloud/ontology": "0.46.0",
65
+ "@segmentstream-cloud/ontology": "0.48.0",
66
66
  "@vercel/oidc": "^3.8.4",
67
67
  "ai": "^7.0.128",
68
68
  "csv-parse": "^7.0.3",
@@ -1,14 +0,0 @@
1
- import { type GroupPolicyInput } from "@segmentstream-cloud/ontology/groups";
2
- import { type Environment } from "../environment.js";
3
- import { type RuntimeToken } from "../runtime-token.js";
4
- export interface CloudGroupSettings {
5
- readonly settings: GroupPolicyInput;
6
- readonly instructions?: string;
7
- }
8
- /** Bounded request-scoped identity, one policy refresh per minute, last known override on transient failure. */
9
- export declare function groupSettings(input?: {
10
- readonly environment?: Environment;
11
- readonly token?: RuntimeToken;
12
- readonly fetch?: typeof fetch;
13
- readonly now?: () => number;
14
- }): () => Promise<CloudGroupSettings | undefined>;
@@ -1,58 +0,0 @@
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 { proxyEnvironment } from "../environment.mjs";
8
- import { runtimeToken } from "../runtime-token.mjs";
9
- import { groupPolicyIssues } from "@segmentstream-cloud/ontology/groups";
10
- function groupSettings(input = {}) {
11
- let cached;
12
- let expires = 0;
13
- let etag;
14
- let pending;
15
- const now = input.now ?? Date.now;
16
- return async () => {
17
- if (now() < expires) return cached;
18
- if (pending !== void 0) return pending;
19
- pending = (async () => {
20
- try {
21
- const env = proxyEnvironment(input.environment);
22
- const identity = await (input.token ?? runtimeToken)();
23
- const workspacePath = `/api/internal/v1/workspaces/${encodeURIComponent(env.workspaceId)}`;
24
- const response = await (input.fetch ?? fetch)(`${env.segmentstreamUrl}${workspacePath}/agent/groups/effective`, {
25
- redirect: "error",
26
- headers: {
27
- "x-segmentstream-organization-id": env.organizationId,
28
- "x-segmentstream-runtime-identity": identity,
29
- ...etag === void 0 ? {} : { "if-none-match": etag }
30
- },
31
- signal: AbortSignal.timeout(5e3)
32
- });
33
- if (response.status === 304) return cached;
34
- if (response.status === 404) return cached;
35
- if (!response.ok) throw new Error(`Group settings read failed (${response.status}); request ${response.headers.get("x-request-id") ?? "unknown"}.`);
36
- const value = await response.json();
37
- if (value.settings === void 0 || groupPolicyIssues(value.settings).length > 0) throw new Error("Invalid group settings response.");
38
- cached = {
39
- settings: value.settings,
40
- ...typeof value.instructions === "string" ? { instructions: value.instructions.slice(0, 6e3) } : {}
41
- };
42
- etag = response.headers.get("etag") ?? (typeof value.version === "string" ? `"${value.version}"` : void 0);
43
- return cached;
44
- } catch (error) {
45
- console.error("[segmentstream] group settings refresh failed", { error });
46
- return cached;
47
- } finally {
48
- expires = now() + 6e4;
49
- }
50
- })();
51
- try {
52
- return await pending;
53
- } finally {
54
- pending = void 0;
55
- }
56
- };
57
- }
58
- export { groupSettings };