@segmentstream/agent 0.25.5 → 0.25.6
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 +8 -5
- package/dist/_chunks/{build-metadata-B_sSM5pA.mjs → build-metadata-C_OoLu9T.mjs} +1 -1
- package/dist/extension/lib/build-metadata.mjs +1 -1
- package/dist/extension/lib/telegram/adapter.mjs +3 -3
- package/dist/extension/lib/telegram/durable-typing.d.ts +4 -0
- package/dist/extension/lib/telegram/durable-typing.mjs +6 -2
- package/dist/extension/lib/telegram/health.mjs +1 -1
- package/dist/extension/lib/telegram/lifecycle.mjs +1 -1
- package/dist/extension/lib/telegram/session-recovery.d.ts +5 -4
- package/dist/extension/lib/telegram/session-recovery.mjs +18 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -717,12 +717,15 @@ See [migration and verification](../../docs/agent-chat-delivery-migration.md).
|
|
|
717
717
|
|
|
718
718
|
If Eve's session lookup raises `RunKeyFetchError` with HTTP 404 and Vercel's
|
|
719
719
|
`not_found` / `Deployment not found.` response, the Telegram adapter records the
|
|
720
|
-
original diagnostic and
|
|
721
|
-
The smart gate still decides whether to reply; observing a message
|
|
722
|
-
and sends no failure notice.
|
|
723
|
-
|
|
724
|
-
|
|
720
|
+
original diagnostic and continues the address at a successor named by the address and
|
|
721
|
+
the unreachable run. The smart gate still decides whether to reply; observing a message
|
|
722
|
+
creates no session and sends no failure notice. Every deployment, independent request and
|
|
723
|
+
cold start selects the same successor, and Eve hands it over between deployments like any
|
|
724
|
+
healthy session. If the successor's own deployment is deleted later, the next successor
|
|
725
|
+
follows the same rule, up to four in a row. Archived conversation context remains
|
|
725
726
|
available, but the inaccessible session's model history and pending inputs cannot migrate.
|
|
727
|
+
`segmentstream history squash` deletes earlier deployments and so ends the sessions
|
|
728
|
+
they own; each affected address continues once at its successor.
|
|
726
729
|
|
|
727
730
|
Recovery applies only to session reads. Dispatch failures are never retried because
|
|
728
731
|
the inbox may already have accepted them. Other provider errors propagate to operator
|
|
@@ -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.25.
|
|
7
|
+
var version = "0.25.6";
|
|
8
8
|
function agentBuild(environment = process.env) {
|
|
9
9
|
return {
|
|
10
10
|
agent: version,
|
|
@@ -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-C_OoLu9T.mjs";
|
|
8
8
|
export { agentBuild };
|
|
@@ -24,7 +24,7 @@ import { MAXIMUM_DOWNLOAD_BYTES, connectionProxy } from "../connection-proxy.mjs
|
|
|
24
24
|
import { dispatchStructuredMessage, structuredMessage } from "./structured.mjs";
|
|
25
25
|
import { scopedAddress, sessionPolicy } from "./sessions.mjs";
|
|
26
26
|
import { handleWorkspaceCallback, isWorkspaceCallback } from "./callbacks.mjs";
|
|
27
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
27
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-C_OoLu9T.mjs";
|
|
28
28
|
import { attachmentLifecycle } from "./lifecycle.mjs";
|
|
29
29
|
import { gatewayImageDescriber } from "./images.mjs";
|
|
30
30
|
import { gatewayAttachmentDescriber } from "./attachment-description.mjs";
|
|
@@ -446,7 +446,7 @@ function segmentstreamTelegram(options) {
|
|
|
446
446
|
return {
|
|
447
447
|
...channel,
|
|
448
448
|
async receive(input, operations) {
|
|
449
|
-
operations = recoverableSessions(operations,
|
|
449
|
+
operations = recoverableSessions(operations, readHistory);
|
|
450
450
|
const target = input.target;
|
|
451
451
|
if (target.confirmationSessionId !== void 0) {
|
|
452
452
|
const address = target.confirmationAddress;
|
|
@@ -516,7 +516,7 @@ function gatedRoute(input) {
|
|
|
516
516
|
if (options.settings() === null) return new Response("not found", { status: 404 });
|
|
517
517
|
args = {
|
|
518
518
|
...args,
|
|
519
|
-
...recoverableSessions(args,
|
|
519
|
+
...recoverableSessions(args, input.readHistory)
|
|
520
520
|
};
|
|
521
521
|
let description;
|
|
522
522
|
const describe = () => description ??= args.describe();
|
|
@@ -11,6 +11,8 @@ export interface TelegramTypingSource {
|
|
|
11
11
|
export interface TypingCursor {
|
|
12
12
|
readonly index: number;
|
|
13
13
|
readonly turnId: string | null;
|
|
14
|
+
/** When the monitor first ran, in epoch milliseconds; continuations carry it forward. */
|
|
15
|
+
readonly startedAt?: number;
|
|
14
16
|
}
|
|
15
17
|
interface TypingEvent {
|
|
16
18
|
readonly type: string;
|
|
@@ -40,6 +42,8 @@ export interface TypingBlockResult {
|
|
|
40
42
|
readonly reason: string;
|
|
41
43
|
readonly sent: number;
|
|
42
44
|
}
|
|
45
|
+
/** The longest a turn shows typing; a longer turn keeps working without the indicator. */
|
|
46
|
+
export declare const MAX_TYPING_MS: number;
|
|
43
47
|
/** One bounded activity. Its durable parent continues independently of the webhook host. */
|
|
44
48
|
export declare function runTypingBlock(source: TelegramTypingSource, cursor: TypingCursor, dependencies: TypingBlockDependencies): Promise<TypingBlockResult>;
|
|
45
49
|
export declare function telegramTypingBlock(source: TelegramTypingSource, cursor: TypingCursor, sessionStatus: string): Promise<TypingBlockResult>;
|
|
@@ -12,18 +12,22 @@ import { p as reportTelegramError } from "../../../_chunks/inbound-request-xmrm-
|
|
|
12
12
|
import { connectionProxy } from "../connection-proxy.mjs";
|
|
13
13
|
import { sessions } from "eve/server";
|
|
14
14
|
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15
|
+
const MAX_TYPING_MS = 18e5;
|
|
15
16
|
async function runTypingBlock(source, cursor, dependencies) {
|
|
16
17
|
const deadline = dependencies.now() + 3e4;
|
|
18
|
+
const startedAt = cursor.startedAt ?? dependencies.now();
|
|
17
19
|
let index = cursor.index, turnId = cursor.turnId ?? source.turnId ?? null, sent = 0;
|
|
18
20
|
const result = (done, reason) => ({
|
|
19
21
|
cursor: {
|
|
20
22
|
index,
|
|
21
|
-
turnId
|
|
23
|
+
turnId,
|
|
24
|
+
startedAt
|
|
22
25
|
},
|
|
23
26
|
done,
|
|
24
27
|
reason,
|
|
25
28
|
sent
|
|
26
29
|
});
|
|
30
|
+
if (dependencies.now() - startedAt >= 18e5) return result(true, "typing_expired");
|
|
27
31
|
if ([
|
|
28
32
|
"completed",
|
|
29
33
|
"failed",
|
|
@@ -175,4 +179,4 @@ async function recordTelegramTypingStart(source, workflowRunId) {
|
|
|
175
179
|
});
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
|
-
export { recordTelegramTypingStart, runTypingBlock, telegramTypingBlock };
|
|
182
|
+
export { MAX_TYPING_MS, recordTelegramTypingStart, runTypingBlock, telegramTypingBlock };
|
|
@@ -5,7 +5,7 @@ const __filename = __eveFileURLToPath(import.meta.url);
|
|
|
5
5
|
__eveDirname(__filename);
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { runtimeRequestFailure } from "./runtime-request.mjs";
|
|
8
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
8
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-C_OoLu9T.mjs";
|
|
9
9
|
import { GET } from "eve/channels";
|
|
10
10
|
const HEALTH_ROUTE = "/eve/v1/telegram/health";
|
|
11
11
|
function healthRoute(input) {
|
|
@@ -10,7 +10,7 @@ import { n as currentInboundRequest, p as reportTelegramError } from "../../../_
|
|
|
10
10
|
import { member } from "../actor.mjs";
|
|
11
11
|
import { needsConfirmation } from "../confirmations.mjs";
|
|
12
12
|
import { OntologyActionNotPermitted, actionToolName, ontologyTools } from "../ontology-tools.mjs";
|
|
13
|
-
import { t as agentBuild } from "../../../_chunks/build-metadata-
|
|
13
|
+
import { t as agentBuild } from "../../../_chunks/build-metadata-C_OoLu9T.mjs";
|
|
14
14
|
var AttachmentLifecycleNotPermitted = class extends Error {
|
|
15
15
|
code = "attachment_lifecycle_not_permitted";
|
|
16
16
|
};
|
|
@@ -2,9 +2,10 @@ import type { ChannelReceiveContext } from "eve/channels";
|
|
|
2
2
|
import type { HistoryReader } from "./sessions.js";
|
|
3
3
|
/**
|
|
4
4
|
* A deleted deployment can make the Workflow metadata key unreadable before Eve can hand off a session.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Eve cannot reset that owner, so the address continues at a successor named by the address and the
|
|
6
|
+
* unreachable run. Every deployment derives the same successor, and Eve hands it over between
|
|
7
|
+
* deployments like any healthy session; if its own deployment is later deleted, the next successor
|
|
8
|
+
* follows the same rule. Recover on a read, before dispatch. Healthy addresses keep their owner.
|
|
8
9
|
* No send is retried: a dispatch error can mean the durable inbox already accepted the message.
|
|
9
10
|
*/
|
|
10
|
-
export declare function recoverableSessions<State>(operations: ChannelReceiveContext<State>,
|
|
11
|
+
export declare function recoverableSessions<State>(operations: ChannelReceiveContext<State>, readHistory?: HistoryReader): ChannelReceiveContext<State>;
|
|
@@ -6,17 +6,18 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { p as reportTelegramError } from "../../../_chunks/inbound-request-xmrm-49O.mjs";
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
|
-
function
|
|
10
|
-
if (!(error instanceof Error) || error.name !== "RunKeyFetchError" || !("status" in error) || error.status !== 404) return
|
|
11
|
-
const
|
|
9
|
+
function strandedRun(error) {
|
|
10
|
+
if (!(error instanceof Error) || error.name !== "RunKeyFetchError" || !("status" in error) || error.status !== 404) return;
|
|
11
|
+
const match = /^Failed to fetch run key for (wrun_[\w-]+) \(deployment dpl_[\w-]+\): HTTP 404 [^\n]+? — (.+)$/u.exec(error.message);
|
|
12
12
|
try {
|
|
13
|
-
const response = JSON.parse(
|
|
14
|
-
return response?.error?.code === "not_found" && response.error.message === "Deployment not found.";
|
|
13
|
+
const response = JSON.parse(match?.[2] ?? "");
|
|
14
|
+
return response?.error?.code === "not_found" && response.error.message === "Deployment not found." ? match?.[1] : void 0;
|
|
15
15
|
} catch {
|
|
16
|
-
return
|
|
16
|
+
return;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
const MAX_SUCCESSORS = 4;
|
|
20
|
+
function recoverableSessions(operations, readHistory) {
|
|
20
21
|
const replacements = new Map();
|
|
21
22
|
const readable = new Map();
|
|
22
23
|
const target = (address) => replacements.get(address) ?? address;
|
|
@@ -33,18 +34,21 @@ function recoverableSessions(operations, deploymentId, readHistory) {
|
|
|
33
34
|
return session;
|
|
34
35
|
}
|
|
35
36
|
async function resolveSession(address) {
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
let current = target(address);
|
|
38
|
+
for (let successors = 0;; successors += 1) try {
|
|
39
|
+
return await lookup(current);
|
|
38
40
|
} catch (error) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const run = successors < MAX_SUCCESSORS ? strandedRun(error) : void 0;
|
|
42
|
+
if (run === void 0) throw error;
|
|
43
|
+
const digest = createHash("sha256").update(JSON.stringify([current, run])).digest("hex");
|
|
44
|
+
const replacement = `${current}:recovery:${digest}`;
|
|
41
45
|
reportTelegramError("session_resume_recovered", error, {
|
|
42
|
-
address,
|
|
46
|
+
address: current,
|
|
43
47
|
replacement,
|
|
44
|
-
|
|
48
|
+
strandedRun: run
|
|
45
49
|
});
|
|
46
50
|
replacements.set(address, replacement);
|
|
47
|
-
|
|
51
|
+
current = replacement;
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
async function source(address) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream/agent",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.6",
|
|
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",
|