eve 0.27.1 → 0.27.3
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/CHANGELOG.md +29 -0
- package/dist/src/channel/session-callback.js +1 -1
- package/dist/src/chunks/{use-eve-agent-PMY2WkAt.js → use-eve-agent-CbF0l_Fp.js} +66 -27
- package/dist/src/chunks/{use-eve-agent-AeQLLhu9.js → use-eve-agent-CgxB9WQv.js} +66 -27
- package/dist/src/cli/commands/build.js +1 -1
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.js +1 -1
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/open-stream.d.ts +15 -2
- package/dist/src/client/open-stream.js +1 -1
- package/dist/src/client/session.d.ts +3 -2
- package/dist/src/client/session.js +1 -1
- package/dist/src/client/types.d.ts +32 -0
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +13 -1
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +5 -0
- package/dist/src/context/keys.js +1 -1
- package/dist/src/execution/node-step.d.ts +6 -0
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/sandbox/bindings/docker.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/terminal-session-failure-step.js +1 -1
- package/dist/src/execution/workflow-callback-url.d.ts +12 -2
- package/dist/src/execution/workflow-callback-url.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-module-evaluation-error.d.ts +2 -0
- package/dist/src/internal/authored-module-evaluation-error.js +4 -0
- package/dist/src/internal/authored-module-loader.js +3 -3
- package/dist/src/internal/authored-package-tsconfig-paths.js +1 -1
- package/dist/src/internal/authored-relative-extension-resolver.d.ts +6 -0
- package/dist/src/internal/authored-relative-extension-resolver.js +1 -0
- package/dist/src/internal/nitro/host/build-application.js +1 -1
- package/dist/src/internal/nitro/host/types.d.ts +9 -0
- package/dist/src/internal/node-esm-compat-banner.js +1 -1
- package/dist/src/internal/workflow-bundle/builder-support.d.ts +5 -0
- package/dist/src/internal/workflow-bundle/builder.js +2 -2
- package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.d.ts +39 -0
- package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.js +1 -0
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +4 -4
- package/dist/src/internal/workflow-bundle/vercel-workflow-output.d.ts +10 -1
- package/dist/src/internal/workflow-bundle/vercel-workflow-output.js +1 -1
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/public/channels/eve.js +2 -1
- package/dist/src/public/channels/linear/inbound-images.d.ts +18 -0
- package/dist/src/public/channels/linear/inbound-images.js +1 -0
- package/dist/src/public/channels/linear/linearChannel.js +1 -1
- package/dist/src/public/channels/slack/api.d.ts +26 -2
- package/dist/src/public/channels/slack/api.js +1 -1
- package/dist/src/public/channels/slack/attachments.d.ts +6 -5
- package/dist/src/public/channels/slack/index.d.ts +1 -1
- package/dist/src/public/channels/slack/interactions.d.ts +2 -1
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/limits.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +51 -14
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/slack/thread.d.ts +4 -3
- package/dist/src/public/channels/slack/thread.js +1 -1
- package/dist/src/public/next/vercel-output-config.js +1 -1
- package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
- package/dist/src/setup/flows/install-vercel-cli.d.ts +8 -2
- package/dist/src/setup/flows/install-vercel-cli.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/vercel-project-api.js +1 -1
- package/dist/src/shared/public-route-prefix.d.ts +23 -0
- package/dist/src/shared/public-route-prefix.js +1 -0
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/channels/linear.mdx +1 -1
- package/docs/channels/slack.mdx +40 -3
- package/docs/extensions.md +2 -2
- package/docs/guides/client/streaming.mdx +15 -0
- package/docs/guides/frontend/nextjs.mdx +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# eve
|
|
2
2
|
|
|
3
|
+
## 0.27.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8168518: Fix connector-auth and remote-subagent callback URLs returning 404 in multi-agent mode. Generated per-agent Vercel services now bake the agent's public route prefix (`/eve/agents/<name>`) into their workflow function environment via `EVE_PUBLIC_ROUTE_PREFIX`, framework-minted callback URLs prepend it, and the session-callback validator accepts callback URLs mounted behind a route prefix so OAuth redirects and remote-subagent session callbacks reach the deployed agent.
|
|
8
|
+
- fecdaf3: Attach authenticated `uploads.linear.app` images from inbound Linear Agent Session markdown as multimodal file parts while preserving the text fallback for untrusted, failed, or non-image URLs.
|
|
9
|
+
- 25c12d3: Prevent bundler-suffixed CommonJS compatibility variables from suppressing the Node ESM globals that eve bundles need at startup.
|
|
10
|
+
- 1bd0aa4: Preserve underscores in connection tool names shown by Slack typing status indicators.
|
|
11
|
+
- 4d3748d: Keep raw outer workflow failure details in the private session trace instead of copying them into provider logs and Workflow telemetry.
|
|
12
|
+
- 4803dbe: Scope authored resolver hooks to relevant import specifiers and reuse filesystem probes for one build. Repeated extensionless misses now perform at most 19 stats once per plugin instance instead of on every resolution.
|
|
13
|
+
- d76f76b: Refresh session-scoped dynamic tools when an existing production session moves to a new Vercel deployment, so added, removed, and reordered tools use the current build.
|
|
14
|
+
- 45ad5d0: Analyze dynamic tool closure references from the syntax tree so object keys and string contents cannot become invalid runtime captures.
|
|
15
|
+
- fc53a9f: Show the number of discovered authored tools in the human-readable `eve info` output.
|
|
16
|
+
- 1c3123c: Write authored sandbox workspace seed files before running bootstrap across the Vercel, Docker, microsandbox, and just-bash backends, so bootstrap can consume canonical workspace inputs and its outputs remain in the captured template.
|
|
17
|
+
- 7094b4a: Load up to 100 Vercel teams per page and use the authenticated API continuation that matches Vercel's emitted cursor, avoiding repeated-page loops for accounts with many teams. When an outdated CLI lacks the required list options, the TUI can upgrade it with the native upgrader and now retains a concise error if that upgrade fails.
|
|
18
|
+
|
|
19
|
+
## 0.27.2
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- 6c433f4: Expose thread-scoped cancellation in Slack message and interaction contexts, plus target-addressed cancellation in `onEvent`, so authored Slack handlers can stop or replace in-flight turns.
|
|
24
|
+
- 835f076: Emit an initial NDJSON whitespace byte when opening a session event stream so clients and proxies receive the response body before the first durable event.
|
|
25
|
+
- f36f143: Added `thread.listParticipants()` for Slack thread routing based on the unique human participants in first-appearance order.
|
|
26
|
+
- eb92ee3: Allow `session.send()` and `session.stream()` callers to disable automatic stream reconnection with `streamReconnectPolicy: { reconnect: false }`, so relays and proxies can own cursor recovery and retry policy.
|
|
27
|
+
- 4133ffc: Slack thread helpers now reuse messages loaded within the same inbound handler, while overlapping `thread.refresh()` calls share one request and failed refreshes preserve the last successful snapshot.
|
|
28
|
+
- ee72db8: Messages posted by the installed Slack app are now ignored before reaching message hooks to prevent self-reply loops.
|
|
29
|
+
- bd4397b: Fixed Slack `threadContext` with `since: "last-agent-reply"` so only replies from the installed app move the context boundary. Replies from other bots remain part of the incremental thread context, are labeled `bot` instead of `agent` in the injected transcript, and their file uploads are now eligible for mention attachment lookback.
|
|
30
|
+
- 64dbe2b: Add actionable authored-module evaluation errors that identify installed packages and distinguish extensionless ESM from missing package output while preserving the original failure as the cause.
|
|
31
|
+
|
|
3
32
|
## 0.27.1
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z}from"#compiled/zod/index.js";import{createEveCallbackRoutePath}from"#protocol/routes.js";import{isReservedIpAddress}from"#shared/network-address.js";const sessionCallbackSchema=z.object({callId:z.string().min(1),subagentName:z.string().min(1),token:z.string().min(1),url:z.string().min(1)}).strict().superRefine((e,t)=>{let r;try{r=new URL(e.url)}catch{t.addIssue({code:`custom`,message:`Callback url must be absolute.`,path:[`url`]});return}readCallbackUrlToken(r)!==e.token&&t.addIssue({code:`custom`,message:`Callback url token must match callback token.`,path:[`url`]}),isReservedIpAddress(r.hostname)&&t.addIssue({code:`custom`,message:`Callback url host must not be a private or reserved address.`,path:[`url`]})});function parseSessionCallback(e){let t=sessionCallbackSchema.safeParse(e);return t.success?{callback:t.data,ok:!0}:{cause:t.error,message:formatSessionCallbackParseError(t.error),ok:!1}}function readCallbackUrlToken(e){let n=createEveCallbackRoutePath(``)
|
|
1
|
+
import{z}from"#compiled/zod/index.js";import{createEveCallbackRoutePath}from"#protocol/routes.js";import{isReservedIpAddress}from"#shared/network-address.js";const sessionCallbackSchema=z.object({callId:z.string().min(1),subagentName:z.string().min(1),token:z.string().min(1),url:z.string().min(1)}).strict().superRefine((e,t)=>{let r;try{r=new URL(e.url)}catch{t.addIssue({code:`custom`,message:`Callback url must be absolute.`,path:[`url`]});return}readCallbackUrlToken(r)!==e.token&&t.addIssue({code:`custom`,message:`Callback url token must match callback token.`,path:[`url`]}),isReservedIpAddress(r.hostname)&&t.addIssue({code:`custom`,message:`Callback url host must not be a private or reserved address.`,path:[`url`]})});function parseSessionCallback(e){let t=sessionCallbackSchema.safeParse(e);return t.success?{callback:t.data,ok:!0}:{cause:t.error,message:formatSessionCallbackParseError(t.error),ok:!1}}function readCallbackUrlToken(e){let n=createEveCallbackRoutePath(``),r=e.pathname.lastIndexOf(n);if(r===-1)return null;let i=e.pathname.slice(r+n.length);if(i.length===0||i.includes(`/`))return null;try{return decodeURIComponent(i)}catch{return null}}function formatSessionCallbackParseError(e){return`Invalid callback metadata: ${e.issues.map(e=>`${e.path.length===0?`callback`:`callback.${e.path.join(`.`)}`}: ${e.message}`).join(`; `)}`}export{parseSessionCallback};
|
|
@@ -471,24 +471,58 @@ function formatSearch(searchParams) {
|
|
|
471
471
|
|
|
472
472
|
//#endregion
|
|
473
473
|
//#region src/client/open-stream.ts
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
474
|
+
const DEFAULT_STREAM_RECONNECT_POLICY = {
|
|
475
|
+
retryableErrorStatuses: new Set([
|
|
476
|
+
404,
|
|
477
|
+
409,
|
|
478
|
+
425,
|
|
479
|
+
500,
|
|
480
|
+
502,
|
|
481
|
+
503,
|
|
482
|
+
504
|
|
483
|
+
]),
|
|
484
|
+
streamIdleReconnectPolicy: {
|
|
485
|
+
baseDelayMs: 250,
|
|
486
|
+
maxAttempts: 5,
|
|
487
|
+
maxDelayMs: 4e3
|
|
488
|
+
},
|
|
489
|
+
streamOpenReconnectPolicy: {
|
|
490
|
+
baseDelayMs: 250,
|
|
491
|
+
maxAttempts: 12,
|
|
492
|
+
maxDelayMs: 5e3
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const NO_STREAM_RECONNECT_POLICY = {
|
|
496
|
+
...DEFAULT_STREAM_RECONNECT_POLICY,
|
|
497
|
+
streamIdleReconnectPolicy: {
|
|
498
|
+
...DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy,
|
|
499
|
+
maxAttempts: 0
|
|
500
|
+
},
|
|
501
|
+
streamOpenReconnectPolicy: {
|
|
502
|
+
...DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy,
|
|
503
|
+
maxAttempts: 1
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
function resolveRetryPolicy(policy, defaults) {
|
|
507
|
+
return {
|
|
508
|
+
...defaults,
|
|
509
|
+
...policy
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function resolveStreamReconnectPolicy(policy) {
|
|
513
|
+
if (policy && "reconnect" in policy && policy.reconnect === false) return NO_STREAM_RECONNECT_POLICY;
|
|
514
|
+
const configured = policy;
|
|
515
|
+
return {
|
|
516
|
+
retryableErrorStatuses: configured?.retryableErrorStatuses ? new Set(configured.retryableErrorStatuses) : DEFAULT_STREAM_RECONNECT_POLICY.retryableErrorStatuses,
|
|
517
|
+
streamIdleReconnectPolicy: resolveRetryPolicy(configured?.streamIdleReconnectPolicy, DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy),
|
|
518
|
+
streamOpenReconnectPolicy: resolveRetryPolicy(configured?.streamOpenReconnectPolicy, DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy)
|
|
519
|
+
};
|
|
520
|
+
}
|
|
489
521
|
async function* followStreamIterable(input) {
|
|
522
|
+
const retryPolicy = resolveStreamReconnectPolicy(input.streamReconnectPolicy);
|
|
523
|
+
const idleRetryPolicy = retryPolicy.streamIdleReconnectPolicy;
|
|
490
524
|
let startIndex = input.startIndex;
|
|
491
|
-
let reconnectDelayMs =
|
|
525
|
+
let reconnectDelayMs = idleRetryPolicy.baseDelayMs;
|
|
492
526
|
let idleReconnects = 0;
|
|
493
527
|
let initialConnection = true;
|
|
494
528
|
while (true) {
|
|
@@ -496,6 +530,7 @@ async function* followStreamIterable(input) {
|
|
|
496
530
|
try {
|
|
497
531
|
body = await openStreamBody({
|
|
498
532
|
...input,
|
|
533
|
+
retryPolicy,
|
|
499
534
|
startIndex
|
|
500
535
|
});
|
|
501
536
|
} catch (error) {
|
|
@@ -507,27 +542,29 @@ async function* followStreamIterable(input) {
|
|
|
507
542
|
for await (const event of readNdjsonStream(body)) {
|
|
508
543
|
startIndex += 1;
|
|
509
544
|
deliveredEvent = true;
|
|
510
|
-
reconnectDelayMs =
|
|
545
|
+
reconnectDelayMs = idleRetryPolicy.baseDelayMs;
|
|
511
546
|
idleReconnects = 0;
|
|
512
547
|
yield event;
|
|
513
548
|
}
|
|
514
549
|
} catch (error) {
|
|
515
550
|
if (!isStreamDisconnectError(error)) throw error;
|
|
516
551
|
}
|
|
517
|
-
if (input.signal?.aborted || input.startIndex < 0) return;
|
|
518
|
-
if (!deliveredEvent && !initialConnection && (idleReconnects += 1) >=
|
|
552
|
+
if (input.signal?.aborted || input.startIndex < 0 || idleRetryPolicy.maxAttempts === 0) return;
|
|
553
|
+
if (!deliveredEvent && !initialConnection && (idleReconnects += 1) >= idleRetryPolicy.maxAttempts) return;
|
|
519
554
|
initialConnection = false;
|
|
520
555
|
await sleep$1(reconnectDelayMs, input.signal);
|
|
521
556
|
if (input.signal?.aborted) return;
|
|
522
|
-
reconnectDelayMs = Math.min(reconnectDelayMs * 2,
|
|
557
|
+
reconnectDelayMs = Math.min(reconnectDelayMs * 2, idleRetryPolicy.maxDelayMs);
|
|
523
558
|
}
|
|
524
559
|
}
|
|
525
560
|
async function openStreamBody(input) {
|
|
561
|
+
const retryPolicy = input.retryPolicy ?? DEFAULT_STREAM_RECONNECT_POLICY;
|
|
562
|
+
const openRetryPolicy = retryPolicy.streamOpenReconnectPolicy;
|
|
526
563
|
let lastStatus;
|
|
527
564
|
let lastBody;
|
|
528
565
|
let lastHeaders;
|
|
529
|
-
let retryDelayMs =
|
|
530
|
-
for (let attempt = 0; attempt <
|
|
566
|
+
let retryDelayMs = openRetryPolicy.baseDelayMs;
|
|
567
|
+
for (let attempt = 0; attempt < openRetryPolicy.maxAttempts; attempt += 1) {
|
|
531
568
|
const url = createClientUrl(input.host, createEveMessageStreamRoutePath(input.sessionId), input.startIndex !== 0 ? { startIndex: String(input.startIndex) } : void 0);
|
|
532
569
|
const headers = await input.resolveHeaders();
|
|
533
570
|
let response;
|
|
@@ -538,9 +575,9 @@ async function openStreamBody(input) {
|
|
|
538
575
|
signal: input.signal ?? null
|
|
539
576
|
});
|
|
540
577
|
} catch (error) {
|
|
541
|
-
if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt ===
|
|
578
|
+
if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt === openRetryPolicy.maxAttempts - 1) throw error;
|
|
542
579
|
await sleep$1(retryDelayMs, input.signal);
|
|
543
|
-
retryDelayMs = Math.min(retryDelayMs * 2,
|
|
580
|
+
retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
|
|
544
581
|
continue;
|
|
545
582
|
}
|
|
546
583
|
if (response.ok) {
|
|
@@ -550,10 +587,10 @@ async function openStreamBody(input) {
|
|
|
550
587
|
lastStatus = response.status;
|
|
551
588
|
lastBody = await response.text();
|
|
552
589
|
lastHeaders = response.headers;
|
|
553
|
-
if (!
|
|
554
|
-
if (attempt <
|
|
590
|
+
if (!retryPolicy.retryableErrorStatuses.has(response.status)) throw new ClientError(response.status, lastBody, response.headers);
|
|
591
|
+
if (attempt < openRetryPolicy.maxAttempts - 1) {
|
|
555
592
|
await sleep$1(retryDelayMs, input.signal);
|
|
556
|
-
retryDelayMs = Math.min(retryDelayMs * 2,
|
|
593
|
+
retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
|
|
557
594
|
}
|
|
558
595
|
}
|
|
559
596
|
throw new ClientError(lastStatus ?? 0, lastBody ?? "Failed to open message stream.", lastHeaders);
|
|
@@ -734,6 +771,7 @@ var ClientSession = class {
|
|
|
734
771
|
host: this.#context.host,
|
|
735
772
|
resolveHeaders: () => this.#context.resolveHeaders(input.headers),
|
|
736
773
|
redirect: this.#context.redirect,
|
|
774
|
+
streamReconnectPolicy: input.streamReconnectPolicy,
|
|
737
775
|
sessionId,
|
|
738
776
|
signal: input.signal,
|
|
739
777
|
startIndex: initialState.sessionId === sessionId ? initialState.streamIndex : 0
|
|
@@ -761,6 +799,7 @@ var ClientSession = class {
|
|
|
761
799
|
host: this.#context.host,
|
|
762
800
|
resolveHeaders: () => this.#context.resolveHeaders(),
|
|
763
801
|
redirect: this.#context.redirect,
|
|
802
|
+
streamReconnectPolicy: options?.streamReconnectPolicy,
|
|
764
803
|
sessionId,
|
|
765
804
|
signal: options?.signal,
|
|
766
805
|
startIndex: streamIndex
|
|
@@ -471,24 +471,58 @@ function formatSearch(searchParams) {
|
|
|
471
471
|
|
|
472
472
|
//#endregion
|
|
473
473
|
//#region src/client/open-stream.ts
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
474
|
+
const DEFAULT_STREAM_RECONNECT_POLICY = {
|
|
475
|
+
retryableErrorStatuses: new Set([
|
|
476
|
+
404,
|
|
477
|
+
409,
|
|
478
|
+
425,
|
|
479
|
+
500,
|
|
480
|
+
502,
|
|
481
|
+
503,
|
|
482
|
+
504
|
|
483
|
+
]),
|
|
484
|
+
streamIdleReconnectPolicy: {
|
|
485
|
+
baseDelayMs: 250,
|
|
486
|
+
maxAttempts: 5,
|
|
487
|
+
maxDelayMs: 4e3
|
|
488
|
+
},
|
|
489
|
+
streamOpenReconnectPolicy: {
|
|
490
|
+
baseDelayMs: 250,
|
|
491
|
+
maxAttempts: 12,
|
|
492
|
+
maxDelayMs: 5e3
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const NO_STREAM_RECONNECT_POLICY = {
|
|
496
|
+
...DEFAULT_STREAM_RECONNECT_POLICY,
|
|
497
|
+
streamIdleReconnectPolicy: {
|
|
498
|
+
...DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy,
|
|
499
|
+
maxAttempts: 0
|
|
500
|
+
},
|
|
501
|
+
streamOpenReconnectPolicy: {
|
|
502
|
+
...DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy,
|
|
503
|
+
maxAttempts: 1
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
function resolveRetryPolicy(policy, defaults) {
|
|
507
|
+
return {
|
|
508
|
+
...defaults,
|
|
509
|
+
...policy
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function resolveStreamReconnectPolicy(policy) {
|
|
513
|
+
if (policy && "reconnect" in policy && policy.reconnect === false) return NO_STREAM_RECONNECT_POLICY;
|
|
514
|
+
const configured = policy;
|
|
515
|
+
return {
|
|
516
|
+
retryableErrorStatuses: configured?.retryableErrorStatuses ? new Set(configured.retryableErrorStatuses) : DEFAULT_STREAM_RECONNECT_POLICY.retryableErrorStatuses,
|
|
517
|
+
streamIdleReconnectPolicy: resolveRetryPolicy(configured?.streamIdleReconnectPolicy, DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy),
|
|
518
|
+
streamOpenReconnectPolicy: resolveRetryPolicy(configured?.streamOpenReconnectPolicy, DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy)
|
|
519
|
+
};
|
|
520
|
+
}
|
|
489
521
|
async function* followStreamIterable(input) {
|
|
522
|
+
const retryPolicy = resolveStreamReconnectPolicy(input.streamReconnectPolicy);
|
|
523
|
+
const idleRetryPolicy = retryPolicy.streamIdleReconnectPolicy;
|
|
490
524
|
let startIndex = input.startIndex;
|
|
491
|
-
let reconnectDelayMs =
|
|
525
|
+
let reconnectDelayMs = idleRetryPolicy.baseDelayMs;
|
|
492
526
|
let idleReconnects = 0;
|
|
493
527
|
let initialConnection = true;
|
|
494
528
|
while (true) {
|
|
@@ -496,6 +530,7 @@ async function* followStreamIterable(input) {
|
|
|
496
530
|
try {
|
|
497
531
|
body = await openStreamBody({
|
|
498
532
|
...input,
|
|
533
|
+
retryPolicy,
|
|
499
534
|
startIndex
|
|
500
535
|
});
|
|
501
536
|
} catch (error) {
|
|
@@ -507,27 +542,29 @@ async function* followStreamIterable(input) {
|
|
|
507
542
|
for await (const event of readNdjsonStream(body)) {
|
|
508
543
|
startIndex += 1;
|
|
509
544
|
deliveredEvent = true;
|
|
510
|
-
reconnectDelayMs =
|
|
545
|
+
reconnectDelayMs = idleRetryPolicy.baseDelayMs;
|
|
511
546
|
idleReconnects = 0;
|
|
512
547
|
yield event;
|
|
513
548
|
}
|
|
514
549
|
} catch (error) {
|
|
515
550
|
if (!isStreamDisconnectError(error)) throw error;
|
|
516
551
|
}
|
|
517
|
-
if (input.signal?.aborted || input.startIndex < 0) return;
|
|
518
|
-
if (!deliveredEvent && !initialConnection && (idleReconnects += 1) >=
|
|
552
|
+
if (input.signal?.aborted || input.startIndex < 0 || idleRetryPolicy.maxAttempts === 0) return;
|
|
553
|
+
if (!deliveredEvent && !initialConnection && (idleReconnects += 1) >= idleRetryPolicy.maxAttempts) return;
|
|
519
554
|
initialConnection = false;
|
|
520
555
|
await sleep$1(reconnectDelayMs, input.signal);
|
|
521
556
|
if (input.signal?.aborted) return;
|
|
522
|
-
reconnectDelayMs = Math.min(reconnectDelayMs * 2,
|
|
557
|
+
reconnectDelayMs = Math.min(reconnectDelayMs * 2, idleRetryPolicy.maxDelayMs);
|
|
523
558
|
}
|
|
524
559
|
}
|
|
525
560
|
async function openStreamBody(input) {
|
|
561
|
+
const retryPolicy = input.retryPolicy ?? DEFAULT_STREAM_RECONNECT_POLICY;
|
|
562
|
+
const openRetryPolicy = retryPolicy.streamOpenReconnectPolicy;
|
|
526
563
|
let lastStatus;
|
|
527
564
|
let lastBody;
|
|
528
565
|
let lastHeaders;
|
|
529
|
-
let retryDelayMs =
|
|
530
|
-
for (let attempt = 0; attempt <
|
|
566
|
+
let retryDelayMs = openRetryPolicy.baseDelayMs;
|
|
567
|
+
for (let attempt = 0; attempt < openRetryPolicy.maxAttempts; attempt += 1) {
|
|
531
568
|
const url = createClientUrl(input.host, createEveMessageStreamRoutePath(input.sessionId), input.startIndex !== 0 ? { startIndex: String(input.startIndex) } : void 0);
|
|
532
569
|
const headers = await input.resolveHeaders();
|
|
533
570
|
let response;
|
|
@@ -538,9 +575,9 @@ async function openStreamBody(input) {
|
|
|
538
575
|
signal: input.signal ?? null
|
|
539
576
|
});
|
|
540
577
|
} catch (error) {
|
|
541
|
-
if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt ===
|
|
578
|
+
if (input.signal?.aborted || !isStreamDisconnectError(error) || attempt === openRetryPolicy.maxAttempts - 1) throw error;
|
|
542
579
|
await sleep$1(retryDelayMs, input.signal);
|
|
543
|
-
retryDelayMs = Math.min(retryDelayMs * 2,
|
|
580
|
+
retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
|
|
544
581
|
continue;
|
|
545
582
|
}
|
|
546
583
|
if (response.ok) {
|
|
@@ -550,10 +587,10 @@ async function openStreamBody(input) {
|
|
|
550
587
|
lastStatus = response.status;
|
|
551
588
|
lastBody = await response.text();
|
|
552
589
|
lastHeaders = response.headers;
|
|
553
|
-
if (!
|
|
554
|
-
if (attempt <
|
|
590
|
+
if (!retryPolicy.retryableErrorStatuses.has(response.status)) throw new ClientError(response.status, lastBody, response.headers);
|
|
591
|
+
if (attempt < openRetryPolicy.maxAttempts - 1) {
|
|
555
592
|
await sleep$1(retryDelayMs, input.signal);
|
|
556
|
-
retryDelayMs = Math.min(retryDelayMs * 2,
|
|
593
|
+
retryDelayMs = Math.min(retryDelayMs * 2, openRetryPolicy.maxDelayMs);
|
|
557
594
|
}
|
|
558
595
|
}
|
|
559
596
|
throw new ClientError(lastStatus ?? 0, lastBody ?? "Failed to open message stream.", lastHeaders);
|
|
@@ -734,6 +771,7 @@ var ClientSession = class {
|
|
|
734
771
|
host: this.#context.host,
|
|
735
772
|
resolveHeaders: () => this.#context.resolveHeaders(input.headers),
|
|
736
773
|
redirect: this.#context.redirect,
|
|
774
|
+
streamReconnectPolicy: input.streamReconnectPolicy,
|
|
737
775
|
sessionId,
|
|
738
776
|
signal: input.signal,
|
|
739
777
|
startIndex: initialState.sessionId === sessionId ? initialState.streamIndex : 0
|
|
@@ -761,6 +799,7 @@ var ClientSession = class {
|
|
|
761
799
|
host: this.#context.host,
|
|
762
800
|
resolveHeaders: () => this.#context.resolveHeaders(),
|
|
763
801
|
redirect: this.#context.redirect,
|
|
802
|
+
streamReconnectPolicy: options?.streamReconnectPolicy,
|
|
764
803
|
sessionId,
|
|
765
804
|
signal: options?.signal,
|
|
766
805
|
startIndex: streamIndex
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolve}from"node:path";import{resolveInternalVercelServiceOutput}from"#cli/vercel-service-output.js";import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";function registerBuildCommand(t){let n=createCliTheme();t.program.command(`build`).description(`Build the current eve application.`).option(`--profile <path>`,`Write best-effort timing and output-size profile JSON to a file`).option(`--skip-sandbox-prewarm`,`Skip sandbox template prewarm for a Vercel build; output may not be deployable`).action(async e=>{let{loadDevelopmentEnvironmentFiles:r}=await import(`#cli/dev/environment.js`);r(t.appRoot);let i=t.buildHost??(await import(`#internal/nitro/host.js`)).buildApplication,a=e.profile===void 0?void 0:resolve(t.appRoot,e.profile),o={skipVercelSandboxPrewarm:e.skipSandboxPrewarm===!0,vercelServiceOutput:resolveInternalVercelServiceOutput(t.appRoot)};a!==void 0&&(o.profileOutputPath=a);let s=await i(t.appRoot,o);t.logger.log(renderCliTaggedLine(n,{message:`built output at ${s}`,tag:`build`,tone:`success`}))})}export{registerBuildCommand};
|
|
1
|
+
import{resolve}from"node:path";import{resolveInternalVercelServiceOutput}from"#cli/vercel-service-output.js";import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{EVE_PUBLIC_ROUTE_PREFIX_ENV,normalizePublicRoutePrefix}from"#shared/public-route-prefix.js";function registerBuildCommand(t){let n=createCliTheme();t.program.command(`build`).description(`Build the current eve application.`).option(`--profile <path>`,`Write best-effort timing and output-size profile JSON to a file`).option(`--skip-sandbox-prewarm`,`Skip sandbox template prewarm for a Vercel build; output may not be deployable`).action(async e=>{let{loadDevelopmentEnvironmentFiles:r}=await import(`#cli/dev/environment.js`);r(t.appRoot);let i=t.buildHost??(await import(`#internal/nitro/host.js`)).buildApplication,a=e.profile===void 0?void 0:resolve(t.appRoot,e.profile),o={publicRoutePrefix:normalizePublicRoutePrefix(process.env[EVE_PUBLIC_ROUTE_PREFIX_ENV]),skipVercelSandboxPrewarm:e.skipSandboxPrewarm===!0,vercelServiceOutput:resolveInternalVercelServiceOutput(t.appRoot)};a!==void 0&&(o.profileOutputPath=a);let s=await i(t.appRoot,o);t.logger.log(renderCliTaggedLine(n,{message:`built output at ${s}`,tag:`build`,tone:`success`}))})}export{registerBuildCommand};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createCliTheme,renderCliBanner,renderCliSection}from"#cli/ui/output.js";import{inspectApplication}from"#services/inspect-application.js";function buildApplicationInfoJson(e){let{application:t,compiledState:n,messaging:r}=e;return{appRoot:t.appRoot,agentRoot:n?.project.agentRoot??null,layout:n?.project.layout??null,status:n?.metadata.status??`unavailable`,diagnostics:n?{errors:n.metadata.discovery.summary.errors,warnings:n.metadata.discovery.summary.warnings}:null,model:n?.manifest.config.model.id??null,instructions:n?.manifest.instructions?.logicalPath??null,skills:(n?.manifest.skills??[]).map(e=>e.name),tools:(n?.manifest.tools??[]).map(e=>e.name),subagents:(n?.manifest.subagents??[]).map(e=>e.name),schedules:(n?.manifest.schedules??[]).map(e=>e.name),channels:(n?.manifest.channels??[]).map(e=>e.kind===`channel`?{name:e.name,kind:e.adapterKind??null,method:e.method,urlPath:e.urlPath}:{name:e.name,kind:`disabled`,method:null,urlPath:null}),messaging:{create:r.createSessionRoutePath,continue:r.continueSessionRoutePattern,stream:r.streamRoutePattern},artifacts:n?{compiledManifest:n.paths.compiledManifestPath,discoveryManifest:n.paths.discoveryManifestPath,diagnostics:n.paths.diagnosticsPath,moduleMap:n.paths.moduleMapPath,metadata:n.paths.compileMetadataPath}:null}}function pluralize(e,t){return`${e} ${t}${e===1?``:`s`}`}function formatDiscoverySummary(e,t){return`${pluralize(e,`error`)}, ${pluralize(t,`warning`)}`}function resolveCompileTone(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function printApplicationInfo(i,a,o={}){let s=await inspectApplication(a);if(o.json){i.log(JSON.stringify(buildApplicationInfoJson(s),null,2));return}let c=s.compiledState,l=s.application,u=createCliTheme(),d=[{label:`App Root`,value:l.appRoot}],f=[{label:`Workflow Build`,value:l.workflowBuildDir},{label:`Output`,value:l.outputDir}],p=[];c===null?d.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(d.push({label:`Agent Root`,value:c.project.agentRoot},{label:`Layout`,value:c.project.layout},{label:`Compile`,tone:resolveCompileTone(c.metadata.status),value:c.metadata.status},{label:`Diagnostics`,tone:c.metadata.discovery.summary.errors>0?`danger`:c.metadata.discovery.summary.warnings>0?`warning`:`success`,value:formatDiscoverySummary(c.metadata.discovery.summary.errors,c.metadata.discovery.summary.warnings)},{label:`Instructions`,value:c.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:pluralize(c.manifest.skills.length,`skill`)},{label:`Subagents`,value:pluralize(c.manifest.subagents.length,`subagent`)},{label:`Schedules`,value:pluralize(c.manifest.schedules.length,`schedule`)}),f.unshift({label:`Compiled Manifest`,value:c.paths.compiledManifestPath},{label:`Discovery Manifest`,value:c.paths.discoveryManifestPath},{label:`Diagnostics`,value:c.paths.diagnosticsPath},{label:`Module Map`,value:c.paths.moduleMapPath},{label:`Metadata`,value:c.paths.compileMetadataPath}),p.push(c.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:c.manifest.instructions.logicalPath})),i.log([renderCliBanner(u,{subtitle:`Resolved application paths and the active message contract.`,title:`eve Info`}),``,renderCliSection(u,{rows:d,title:`Application`}),``,renderCliSection(u,{rows:f,title:`Artifacts`}),...c===null?[]:[``,renderCliSection(u,{rows:p,title:`Instructions`})],``,renderCliSection(u,{rows:[{label:`Workflow ID`,value:l.workflowId},{label:`Source Dir`,value:l.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${s.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${s.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${s.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
|
|
1
|
+
import{createCliTheme,renderCliBanner,renderCliSection}from"#cli/ui/output.js";import{inspectApplication}from"#services/inspect-application.js";function buildApplicationInfoJson(e){let{application:t,compiledState:n,messaging:r}=e;return{appRoot:t.appRoot,agentRoot:n?.project.agentRoot??null,layout:n?.project.layout??null,status:n?.metadata.status??`unavailable`,diagnostics:n?{errors:n.metadata.discovery.summary.errors,warnings:n.metadata.discovery.summary.warnings}:null,model:n?.manifest.config.model.id??null,instructions:n?.manifest.instructions?.logicalPath??null,skills:(n?.manifest.skills??[]).map(e=>e.name),tools:(n?.manifest.tools??[]).map(e=>e.name),subagents:(n?.manifest.subagents??[]).map(e=>e.name),schedules:(n?.manifest.schedules??[]).map(e=>e.name),channels:(n?.manifest.channels??[]).map(e=>e.kind===`channel`?{name:e.name,kind:e.adapterKind??null,method:e.method,urlPath:e.urlPath}:{name:e.name,kind:`disabled`,method:null,urlPath:null}),messaging:{create:r.createSessionRoutePath,continue:r.continueSessionRoutePattern,stream:r.streamRoutePattern},artifacts:n?{compiledManifest:n.paths.compiledManifestPath,discoveryManifest:n.paths.discoveryManifestPath,diagnostics:n.paths.diagnosticsPath,moduleMap:n.paths.moduleMapPath,metadata:n.paths.compileMetadataPath}:null}}function pluralize(e,t){return`${e} ${t}${e===1?``:`s`}`}function formatDiscoverySummary(e,t){return`${pluralize(e,`error`)}, ${pluralize(t,`warning`)}`}function resolveCompileTone(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function printApplicationInfo(i,a,o={}){let s=await inspectApplication(a);if(o.json){i.log(JSON.stringify(buildApplicationInfoJson(s),null,2));return}let c=s.compiledState,l=s.application,u=createCliTheme(),d=[{label:`App Root`,value:l.appRoot}],f=[{label:`Workflow Build`,value:l.workflowBuildDir},{label:`Output`,value:l.outputDir}],p=[];c===null?d.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(d.push({label:`Agent Root`,value:c.project.agentRoot},{label:`Layout`,value:c.project.layout},{label:`Compile`,tone:resolveCompileTone(c.metadata.status),value:c.metadata.status},{label:`Diagnostics`,tone:c.metadata.discovery.summary.errors>0?`danger`:c.metadata.discovery.summary.warnings>0?`warning`:`success`,value:formatDiscoverySummary(c.metadata.discovery.summary.errors,c.metadata.discovery.summary.warnings)},{label:`Instructions`,value:c.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:pluralize(c.manifest.skills.length,`skill`)},{label:`Tools`,value:pluralize(c.manifest.tools.length,`tool`)},{label:`Subagents`,value:pluralize(c.manifest.subagents.length,`subagent`)},{label:`Schedules`,value:pluralize(c.manifest.schedules.length,`schedule`)}),f.unshift({label:`Compiled Manifest`,value:c.paths.compiledManifestPath},{label:`Discovery Manifest`,value:c.paths.discoveryManifestPath},{label:`Diagnostics`,value:c.paths.diagnosticsPath},{label:`Module Map`,value:c.paths.moduleMapPath},{label:`Metadata`,value:c.paths.compileMetadataPath}),p.push(c.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:c.manifest.instructions.logicalPath})),i.log([renderCliBanner(u,{subtitle:`Resolved application paths and the active message contract.`,title:`eve Info`}),``,renderCliSection(u,{rows:d,title:`Application`}),``,renderCliSection(u,{rows:f,title:`Artifacts`}),...c===null?[]:[``,renderCliSection(u,{rows:p,title:`Instructions`})],``,renderCliSection(u,{rows:[{label:`Workflow ID`,value:l.workflowId},{label:`Source Dir`,value:l.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${s.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${s.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${s.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
|
|
2
2
|
`))}export{buildApplicationInfoJson,printApplicationInfo};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{createTuiPrompter}from"./tui-prompter.js";import{runDeployFlow}from"#setup/flows/deploy.js";import{WizardCancelledError}from"#setup/step.js";import{runLoginFlow}from"#setup/flows/login.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{runChannelsFlow}from"#setup/flows/channels.js";import{runConnectionsFlow}from"#setup/flows/connections.js";import{runInstallVercelCliFlow}from"#setup/flows/install-vercel-cli.js";import{runModelFlow}from"#setup/flows/model.js";import{runProviderFlow}from"#setup/flows/provider.js";import{openUrl}from"#setup/primitives/open-url.js";import{slackMessageDeepLink}from"#setup/slack-connect.js";const SETUP_FLOW_CONFIG={"vc:install":{title:`Install the Vercel CLI`,indicator:`pulse`},"vc:login":{title:`Log in to Vercel`,indicator:`pulse`},model:{title:`Configure the agent model`,indicator:`pulse`},channels:{title:`Agent channels`,indicator:`pulse`},connect:{title:`Agent connections`,indicator:`pulse`},deploy:{title:`Deploy to Vercel`,indicator:`spinner`}};function muteableRenderer(e,t){return{readSelect:n=>t()?Promise.resolve(void 0):e.readSelect(n),readEditableSelect:n=>t()?Promise.resolve(void 0):e.readEditableSelect(n),readProviderPicker:n=>t()?Promise.resolve(void 0):e.readProviderPicker(n),readModelEditor:n=>t()?Promise.resolve(void 0):e.readModelEditor(n),readText:n=>t()?Promise.resolve(void 0):e.readText(n),readAcknowledge:n=>t()?Promise.resolve():e.readAcknowledge(n),readChoice:n=>t()?{choice:Promise.resolve(void 0),close:()=>{}}:e.readChoice(n),setStatus:n=>{t()||e.setStatus(n)},renderLine:(n,r)=>{(!t()||r===`warning`||r===`error`)&&e.renderLine(n,r)},renderOutput:n=>{t()||e.renderOutput(n)}}}async function runTuiSetupCommand(t){let{command:r}=t,i=!1,a=new AbortController,o=muteableRenderer(t.renderer,()=>i),s=(t.createPrompter??createTuiPrompter)(o),c=t.renderer.waitForInterrupt(),l=Symbol(`interrupted`),u=executeSetupCommand(t,s,o,a.signal);try{let e=await Promise.race([u,c.promise.then(()=>l)]);return e===l?(i=!0,a.abort(new WizardCancelledError),{...await u,message:`/${r} interrupted.`,preserveFlowDiagnostics:!0}):e}finally{c.dispose(),t.renderer.setStatus(void 0)}}async function executeSetupCommand(e,i,u,d){let{command:f,appRoot:p}=e,m={runInstallVercelCliFlow,runLoginFlow,runModelFlow,runChannelsFlow,runConnectionsFlow,runDeployFlow,...e.flows};try{switch(f){case`vc:install`:return installVercelCliResultMessage(await m.runInstallVercelCliFlow({appRoot:p,prompter:i,signal:d}));case`vc:login`:return loginResultMessage(await m.runLoginFlow({appRoot:p,prompter:i,signal:d}));case`model`:{let pickProvider=e=>u.readProviderPicker(e),t={appRoot:p,prompter:i,signal:d,deps:{pickModelSettings:e=>u.readModelEditor(e),runProviderFlow:e=>runProviderFlow({...e,picker:pickProvider})}};e.initialModelStep!==void 0&&(t.initialStep=e.initialModelStep);let n=await m.runModelFlow(t);if(n.kind===`cancelled`)return{message:n.discardedDraft===!0?`/model dismissed. Drafted changes were discarded; Done commits them.`:`/model dismissed.`,preserveFlowDiagnostics:!1};let r=[];n.modelMessage!==void 0&&r.push(n.modelMessage),n.providerOutcome!==void 0&&r.push(providerOutcomeMessage(n.providerOutcome));let a={message:r.join(`
|
|
2
|
-
`),preserveFlowDiagnostics:!1};return n.providerOutcome!==void 0&&(a.effect={kind:`model-access-changed`}),a}case`channels`:{let e=await m.runChannelsFlow({appRoot:p,prompter:i,signal:d});switch(e.kind){case`failed`:return pendingChannelsResult(`Channel files changed, but /channels failed: ${e.message}`);case`cancelled`:return{message:`/channels dismissed.`,preserveFlowDiagnostics:!0};case`deploy-and-chat`:return await runDeployAndChat(m,{appRoot:p,prompter:i,signal:d},e.chat);case`done`:return e.addedChannels.length===0?{message:`No channels added.`,preserveFlowDiagnostics:!0}:{message:`Channels added: ${e.addedChannels.join(`, `)} — run /deploy to ship them.`,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}}case`connect`:{let t=await m.runConnectionsFlow({appRoot:p,prompter:i,signal:d,disabledConnectionReasons:e.disabledConnectionReasons});switch(t.kind){case`cancelled`:return{message:`/connect dismissed.`,preserveFlowDiagnostics:!0,effect:{kind:`model-access-changed`}};case`failed`:return{message:t.addedConnections.length===0?`/connect failed: ${t.message}`:`Connection files changed, but /connect failed: ${t.message}`,preserveFlowDiagnostics:!0,effect:t.addedConnections.length===0?{kind:`model-access-changed`}:{kind:`connection-added`}};case`done`:return{message:t.addedConnections.length===0?`No connections added.`:`Connections added: ${t.addedConnections.join(`, `)}.`,preserveFlowDiagnostics:!0,effect:t.addedConnections.length===0?{kind:`model-access-changed`}:{kind:`connection-added`}}}}case`deploy`:{let e=await m.runDeployFlow({appRoot:p,prompter:i,interactive:!0,signal:d});return e.kind===`cancelled`?{message:`/deploy dismissed.`,preserveFlowDiagnostics:!0}:e.kind===`needs-link`?{message:`Not linked to a Vercel project — run /model to connect one first.`,preserveFlowDiagnostics:!0}:{message:e.productionUrl===void 0?`Deployed.`:`Deployed: ${e.productionUrl}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}}}catch(e){if(e instanceof WizardCancelledError)return{message:`/${f} dismissed.`,preserveFlowDiagnostics:f!==`model`};let t=vercelActionOutcome(e,f);return
|
|
2
|
+
`),preserveFlowDiagnostics:!1};return n.providerOutcome!==void 0&&(a.effect={kind:`model-access-changed`}),a}case`channels`:{let e=await m.runChannelsFlow({appRoot:p,prompter:i,signal:d});switch(e.kind){case`failed`:return pendingChannelsResult(`Channel files changed, but /channels failed: ${e.message}`);case`cancelled`:return{message:`/channels dismissed.`,preserveFlowDiagnostics:!0};case`deploy-and-chat`:return await runDeployAndChat(m,{appRoot:p,prompter:i,signal:d},e.chat);case`done`:return e.addedChannels.length===0?{message:`No channels added.`,preserveFlowDiagnostics:!0}:{message:`Channels added: ${e.addedChannels.join(`, `)} — run /deploy to ship them.`,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}}case`connect`:{let t=await m.runConnectionsFlow({appRoot:p,prompter:i,signal:d,disabledConnectionReasons:e.disabledConnectionReasons});switch(t.kind){case`cancelled`:return{message:`/connect dismissed.`,preserveFlowDiagnostics:!0,effect:{kind:`model-access-changed`}};case`failed`:return{message:t.addedConnections.length===0?`/connect failed: ${t.message}`:`Connection files changed, but /connect failed: ${t.message}`,preserveFlowDiagnostics:!0,effect:t.addedConnections.length===0?{kind:`model-access-changed`}:{kind:`connection-added`}};case`done`:return{message:t.addedConnections.length===0?`No connections added.`:`Connections added: ${t.addedConnections.join(`, `)}.`,preserveFlowDiagnostics:!0,effect:t.addedConnections.length===0?{kind:`model-access-changed`}:{kind:`connection-added`}}}}case`deploy`:{let e=await m.runDeployFlow({appRoot:p,prompter:i,interactive:!0,signal:d});return e.kind===`cancelled`?{message:`/deploy dismissed.`,preserveFlowDiagnostics:!0}:e.kind===`needs-link`?{message:`Not linked to a Vercel project — run /model to connect one first.`,preserveFlowDiagnostics:!0}:{message:e.productionUrl===void 0?`Deployed.`:`Deployed: ${e.productionUrl}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}}}catch(e){if(e instanceof WizardCancelledError)return{message:`/${f} dismissed.`,preserveFlowDiagnostics:f!==`model`};let t=await vercelCliUpgradeOutcome(e,f,m,{appRoot:p,prompter:i,signal:d});if(t!==void 0)return t;let r=vercelActionOutcome(e,f);return r===void 0?{message:`/${f} failed: ${e instanceof Error?e.message:String(e)}`,preserveFlowDiagnostics:!0}:r}}async function vercelCliUpgradeOutcome(e,t,n,r){if(!(e instanceof HumanActionRequiredError)||e.action.kind!==`vercel-cli-upgrade`)return;let a;try{a=await r.prompter.select({message:`Your Vercel CLI needs an update to list your teams. Upgrade now?`,options:[{value:`upgrade`,label:`Upgrade Vercel CLI`,description:`Run the Vercel CLI's native upgrader`},{value:`later`,label:`Not now`}],initialValue:`upgrade`})}catch{a=`later`}if(a===`later`)return{message:`The Vercel CLI needs an update — run \`vercel upgrade\`, then retry /${t}.`,preserveFlowDiagnostics:!0};let o;try{o=await n.runInstallVercelCliFlow({appRoot:r.appRoot,prompter:r.prompter,signal:r.signal,upgrade:!0})}catch(e){return{message:vercelCliUpgradeFailureMessage(t,errorMessage(e)),preserveFlowDiagnostics:!0}}switch(o.kind){case`installed`:return{message:`Upgraded the Vercel CLI. Retry /${t}.`,preserveFlowDiagnostics:!1};case`failed`:return{message:vercelCliUpgradeFailureMessage(t,o.reason),preserveFlowDiagnostics:!0};case`cancelled`:return{message:`Vercel CLI upgrade cancelled — run \`vercel upgrade\`, then retry /${t}.`,preserveFlowDiagnostics:!0};case`already`:return{message:`The Vercel CLI is already up to date. Retry /${t}.`,preserveFlowDiagnostics:!1}}}function errorMessage(e){let t=(e instanceof Error?e.message:String(e)).replace(/\s+/gu,` `).trim();return t.length<=240?t:`${t.slice(0,239)}…`}function vercelCliUpgradeFailureMessage(e,t){return`Couldn't upgrade the Vercel CLI${t===void 0||t===``?``:` (${t})`} — run \`vercel upgrade\`, then retry /${e}.`}function vercelActionOutcome(e,t){if(!(e instanceof HumanActionRequiredError))return;let n=vercelActionMessage(e.action.kind,t);return n===void 0?void 0:{message:n,preserveFlowDiagnostics:!0}}function vercelActionMessage(e,t){switch(e){case`vercel-login`:return`You're not logged in to Vercel — run /vc:login, then retry /${t}.`;case`vercel-forbidden`:return`Vercel denied access to that team — run /vc:login to re-authenticate (for example to complete SSO), or pick a team you can access, then retry /${t}.`;case`vercel-cli-missing`:return`The Vercel CLI isn't installed — run /vc:install to install it, then retry /${t}.`;case`vercel-cli-upgrade`:return`The Vercel CLI needs an update — run \`vercel upgrade\`, then retry /${t}.`;default:return}}async function runDeployAndChat(e,t,r){let i;try{i=await e.runDeployFlow({...t,interactive:!0})}catch(e){if(e instanceof WizardCancelledError)return pendingChannelsResult(`Channels added, but /deploy was dismissed. Run /deploy to ship them.`);let t=vercelActionOutcome(e,`deploy`);return pendingChannelsResult(t===void 0?`Channels added, but /deploy failed: ${e instanceof Error?e.message:String(e)}`:`Channels added. ${t.message}`)}if(i.kind===`cancelled`)return pendingChannelsResult(`Channels added, but /deploy was dismissed. Run /deploy to ship them.`);if(i.kind===`needs-link`)return pendingChannelsResult(`Channels added, but this directory is not linked to Vercel. Run /model, then /deploy.`);let a=i.productionUrl===void 0?`Deployed.`:`Deployed: ${i.productionUrl}`,o;if(r.chatUrl===void 0)o=`Message your agent in Slack to see it live.`;else{let e=slackMessageDeepLink(r.chatUrl);openUrl(e),o=`Chat with your agent in Slack: ${e}`}return{message:`${a}\n${o}`,preserveFlowDiagnostics:!0,effect:{kind:`deployed`}}}function installVercelCliResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/vc:install dismissed.`,preserveFlowDiagnostics:!1};case`already`:return{message:`The Vercel CLI is already installed.`,preserveFlowDiagnostics:!1};case`failed`:return{message:"Couldn't install the Vercel CLI — install it manually with `npm i -g vercel@latest`.",preserveFlowDiagnostics:!0};case`installed`:return{message:`Installed the Vercel CLI. Run /vc:login next.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}}}}function loginResultMessage(e){switch(e.kind){case`cancelled`:return{message:`/vc:login dismissed.`,preserveFlowDiagnostics:!1};case`already`:return{message:`You're already logged in to Vercel.`,preserveFlowDiagnostics:!1};case`cli-missing`:return{message:`The Vercel CLI isn't installed — run /vc:install to install it, then retry /vc:login.`,preserveFlowDiagnostics:!0};case`failed`:return{message:`Vercel login didn't complete — run /vc:login to try again.`,preserveFlowDiagnostics:!0};case`logged-in`:return{message:`Logged in to Vercel.`,preserveFlowDiagnostics:!1,effect:{kind:`refresh-identity`}};case`unavailable`:return{message:`Couldn't reach Vercel — check your connection, then retry /vc:login.`,preserveFlowDiagnostics:!0}}}function pendingChannelsResult(e){return{message:e,preserveFlowDiagnostics:!0,effect:{kind:`channels-added`}}}function providerOutcomeMessage(e){let{resolution:t,status:n}=e;if(n.kind===`gateway-project`)return t===void 0?`Project linked. No model credential found; set AI_GATEWAY_API_KEY in .env.local.`:t.credential===`oidc`?`Project linked. Connected to AI Gateway via VERCEL_OIDC_TOKEN.`:t.shadowedOidc===void 0?`Project linked. Connected to AI Gateway via AI_GATEWAY_API_KEY.`:`Project linked. AI_GATEWAY_API_KEY (${t.source.kind===`shell`?`shell`:t.source.path}) outranks the project's VERCEL_OIDC_TOKEN and stays the active credential — ${t.source.kind===`shell`?`unset it in your shell`:`remove it from ${t.source.path}`} to run on the project.`;if(n.kind===`gateway-key`){let e=n.source.kind===`shell`?`your shell`:n.source.path;return`Connected to AI Gateway via ${n.envKey} in ${e}.`}return`Provider updated — no gateway credential detected; set AI_GATEWAY_API_KEY in .env.local.`}export{SETUP_FLOW_CONFIG,runTuiSetupCommand};
|
|
@@ -7,7 +7,7 @@ export { createDataUrlFilePart, createTextWithFileContent } from "#client/file-p
|
|
|
7
7
|
export { MessageResponse } from "#client/message-response.js";
|
|
8
8
|
export { ClientSession } from "#client/session.js";
|
|
9
9
|
export type { EveAgentStoreCallbacks, EveAgentStoreInit, EveAgentStoreSnapshot, EveAgentStoreStatus, PrepareSend, } from "#client/eve-agent-store.js";
|
|
10
|
-
export type { AgentInfoEntry, AgentInfoChannelEntry, AgentInfoChannels, AgentInfoConnectionEntry, AgentInfoDynamicResolverEntry, AgentInfoFrameworkChannelEntry, AgentInfoFrameworkToolEntry, AgentInfoHookEntry, AgentInfoInstructions, AgentInfoInstructionsEntry, AgentInfoResult, AgentInfoSandboxEntry, AgentInfoScheduleEntry, AgentInfoSkillEntry, AgentInfoSource, AgentInfoSubagentEntry, AgentInfoToolEntry, AgentInfoTools, CancelSessionResult, ClientAuth, ClientOptions, ClientRedirectPolicy, HeadersValue, HealthResult, MessageResult, SendTurnInput, SendTurnPayload, SessionState, StreamOptions, TokenValue, } from "#client/types.js";
|
|
10
|
+
export type { AgentInfoEntry, AgentInfoChannelEntry, AgentInfoChannels, AgentInfoConnectionEntry, AgentInfoDynamicResolverEntry, AgentInfoFrameworkChannelEntry, AgentInfoFrameworkToolEntry, AgentInfoHookEntry, AgentInfoInstructions, AgentInfoInstructionsEntry, AgentInfoResult, AgentInfoSandboxEntry, AgentInfoScheduleEntry, AgentInfoSkillEntry, AgentInfoSource, AgentInfoSubagentEntry, AgentInfoToolEntry, AgentInfoTools, CancelSessionResult, ClientAuth, ClientOptions, ClientRedirectPolicy, HeadersValue, HealthResult, MessageResult, ResolvedStreamReconnectPolicy, SendTurnInput, SendTurnPayload, SessionState, StreamOptions, StreamReconnectPolicy, StreamReconnectRetryPolicy, TokenValue, } from "#client/types.js";
|
|
11
11
|
export type { EveAgentReducer, EveAgentReducerEvent, ClientInputRespondedEvent, ClientMessageFailedEvent, ClientMessageSubmittedEvent, } from "#client/reducer.js";
|
|
12
12
|
export type { EveAuthorizationChallenge, EveAuthorizationOutcome, EveAuthorizationPart, EveMessageData, EveDynamicToolPart, EveMessageInputRequest, EveMessage, EveMessageMetadata, EveMessagePart, EveMessageToolMetadata, } from "#client/message-reducer.js";
|
|
13
13
|
export type { ActionResultStreamEvent, ActionsRequestedStreamEvent, AssistantStepFinishReason, AuthorizationOutcome, CompactionCompletedStreamEvent, CompactionRequestedStreamEvent, AuthorizationCompletedStreamEvent, ConnectionAuthorizationOutcome, AuthorizationRequiredStreamEvent, HandleMessageStreamEvent, InputRequestedStreamEvent, MessageAppendedStreamEvent, MessageCompletedStreamEvent, MessageReceivedPart, MessageReceivedStreamEvent, ReasoningAppendedStreamEvent, ReasoningCompletedStreamEvent, ResultCompletedStreamEvent, SessionCompletedStreamEvent, SessionFailedStreamEvent, SessionStartedStreamEvent, SessionWaitingStreamEvent, StepCompletedStreamEvent, StepFailedStreamEvent, StepStartedStreamEvent, SubagentCalledStreamEvent, SubagentChildEventStreamEvent, SubagentCompletedStreamEvent, SubagentStartedStreamEvent, TurnCancelledStreamEvent, TurnCompletedStreamEvent, TurnFailedStreamEvent, TurnStartedStreamEvent, TurnFailureStreamEvent, } from "#protocol/message.js";
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
2
|
-
import type { ClientRedirectPolicy } from "#client/types.js";
|
|
2
|
+
import type { ClientRedirectPolicy, StreamReconnectPolicy } from "#client/types.js";
|
|
3
|
+
interface RetryPolicy {
|
|
4
|
+
readonly baseDelayMs: number;
|
|
5
|
+
readonly maxAttempts: number;
|
|
6
|
+
readonly maxDelayMs: number;
|
|
7
|
+
}
|
|
8
|
+
interface ResolvedStreamReconnectPolicy {
|
|
9
|
+
readonly retryableErrorStatuses: ReadonlySet<number>;
|
|
10
|
+
readonly streamIdleReconnectPolicy: RetryPolicy;
|
|
11
|
+
readonly streamOpenReconnectPolicy: RetryPolicy;
|
|
12
|
+
}
|
|
3
13
|
/**
|
|
4
14
|
* Internal configuration for following a durable event stream.
|
|
5
15
|
*/
|
|
6
16
|
interface FollowStreamInput {
|
|
7
17
|
readonly host: string;
|
|
18
|
+
readonly streamReconnectPolicy?: StreamReconnectPolicy;
|
|
8
19
|
readonly resolveHeaders: () => Promise<Headers>;
|
|
9
20
|
readonly redirect?: ClientRedirectPolicy;
|
|
10
21
|
readonly sessionId: string;
|
|
@@ -27,5 +38,7 @@ export declare function followStreamIterable(input: FollowStreamInput): AsyncGen
|
|
|
27
38
|
* propagation window where a just-acknowledged session may not yet be
|
|
28
39
|
* readable from the stream route.
|
|
29
40
|
*/
|
|
30
|
-
export declare function openStreamBody(input: FollowStreamInput
|
|
41
|
+
export declare function openStreamBody(input: FollowStreamInput & {
|
|
42
|
+
readonly retryPolicy?: ResolvedStreamReconnectPolicy;
|
|
43
|
+
}): Promise<ReadableStream<Uint8Array>>;
|
|
31
44
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createEveMessageStreamRoutePath}from"#protocol/routes.js";import{ClientError}from"#client/client-error.js";import{createClientUrl}from"#client/url.js";import{isStreamDisconnectError,readNdjsonStream}from"#client/ndjson.js";const
|
|
1
|
+
import{createEveMessageStreamRoutePath}from"#protocol/routes.js";import{ClientError}from"#client/client-error.js";import{createClientUrl}from"#client/url.js";import{isStreamDisconnectError,readNdjsonStream}from"#client/ndjson.js";const DEFAULT_STREAM_RECONNECT_POLICY={retryableErrorStatuses:new Set([404,409,425,500,502,503,504]),streamIdleReconnectPolicy:{baseDelayMs:250,maxAttempts:5,maxDelayMs:4e3},streamOpenReconnectPolicy:{baseDelayMs:250,maxAttempts:12,maxDelayMs:5e3}},NO_STREAM_RECONNECT_POLICY={...DEFAULT_STREAM_RECONNECT_POLICY,streamIdleReconnectPolicy:{...DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy,maxAttempts:0},streamOpenReconnectPolicy:{...DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy,maxAttempts:1}};function resolveRetryPolicy(e,t){return{...t,...e}}function resolveStreamReconnectPolicy(e){if(e&&`reconnect`in e&&e.reconnect===!1)return NO_STREAM_RECONNECT_POLICY;let t=e;return{retryableErrorStatuses:t?.retryableErrorStatuses?new Set(t.retryableErrorStatuses):DEFAULT_STREAM_RECONNECT_POLICY.retryableErrorStatuses,streamIdleReconnectPolicy:resolveRetryPolicy(t?.streamIdleReconnectPolicy,DEFAULT_STREAM_RECONNECT_POLICY.streamIdleReconnectPolicy),streamOpenReconnectPolicy:resolveRetryPolicy(t?.streamOpenReconnectPolicy,DEFAULT_STREAM_RECONNECT_POLICY.streamOpenReconnectPolicy)}}async function*followStreamIterable(e){let t=resolveStreamReconnectPolicy(e.streamReconnectPolicy),n=t.streamIdleReconnectPolicy,a=e.startIndex,o=n.baseDelayMs,s=0,c=!0;for(;;){let l;try{l=await openStreamBody({...e,retryPolicy:t,startIndex:a})}catch(t){if(e.signal?.aborted)return;throw t}let u=!1;try{for await(let e of readNdjsonStream(l))a+=1,u=!0,o=n.baseDelayMs,s=0,yield e}catch(e){if(!isStreamDisconnectError(e))throw e}if(e.signal?.aborted||e.startIndex<0||n.maxAttempts===0||!u&&!c&&(s+=1)>=n.maxAttempts||(c=!1,await sleep(o,e.signal),e.signal?.aborted))return;o=Math.min(o*2,n.maxDelayMs)}}async function openStreamBody(i){let o=i.retryPolicy??DEFAULT_STREAM_RECONNECT_POLICY,s=o.streamOpenReconnectPolicy,c,l,u,d=s.baseDelayMs;for(let a=0;a<s.maxAttempts;a+=1){let f=createClientUrl(i.host,createEveMessageStreamRoutePath(i.sessionId),i.startIndex===0?void 0:{startIndex:String(i.startIndex)}),p=await i.resolveHeaders(),m;try{m=await fetch(f,{headers:p,redirect:i.redirect,signal:i.signal??null})}catch(e){if(i.signal?.aborted||!isStreamDisconnectError(e)||a===s.maxAttempts-1)throw e;await sleep(d,i.signal),d=Math.min(d*2,s.maxDelayMs);continue}if(m.ok){if(!m.body)throw new ClientError(m.status,`Response body is null.`,m.headers);return m.body}if(c=m.status,l=await m.text(),u=m.headers,!o.retryableErrorStatuses.has(m.status))throw new ClientError(m.status,l,m.headers);a<s.maxAttempts-1&&(await sleep(d,i.signal),d=Math.min(d*2,s.maxDelayMs))}throw new ClientError(c??0,l??`Failed to open message stream.`,u)}async function sleep(e,t){t?.aborted||await new Promise(n=>{let onAbort=()=>{clearTimeout(r),n()},r=setTimeout(()=>{t?.removeEventListener(`abort`,onAbort),n()},e);t?.addEventListener(`abort`,onAbort,{once:!0})})}export{followStreamIterable,openStreamBody};
|
|
@@ -55,8 +55,9 @@ export declare class ClientSession {
|
|
|
55
55
|
* Opens this session's event stream for the current session ID.
|
|
56
56
|
*
|
|
57
57
|
* Resumes from the session's stored stream cursor unless `options.startIndex`
|
|
58
|
-
* overrides it.
|
|
59
|
-
* ends
|
|
58
|
+
* overrides it. By default, the stream reconnects from its cursor when the
|
|
59
|
+
* connection ends; pass `streamReconnectPolicy: { reconnect: false }` to use
|
|
60
|
+
* one connection. Negative indices read relative to the current tail on one connection
|
|
60
61
|
* and do not advance the stored absolute cursor.
|
|
61
62
|
*
|
|
62
63
|
* @throws {Error} If the session has no session ID (no message has been sent
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EVE_CREATE_SESSION_ROUTE_PATH,createEveCancelTurnRoutePath,createEveContinueSessionRoutePath}from"#protocol/routes.js";import{EVE_SESSION_ID_HEADER,isCurrentTurnBoundaryEvent}from"#protocol/message.js";import{ClientError}from"#client/client-error.js";import{advanceSession}from"#client/session-utils.js";import{createClientUrl}from"#client/url.js";import{MessageResponse}from"#client/message-response.js";import{CancelTurnResponseSchema}from"#protocol/cancel-turn.js";import{followStreamIterable}from"#client/open-stream.js";import{serializeOutputSchema}from"#shared/tool-schema.js";var ClientSession=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}get state(){return this.#t}async send(e){let t=normalizeSendTurnInput(e),n=this.#t,{continuationToken:r,sessionId:i}=await this.#n(t,n);return this.#t===n&&(this.#t={...n,sessionId:i}),new MessageResponse({continuationToken:r,createStream:()=>this.#r(i,r,n,t),sessionId:i})}async cancel(e){let n=this.#t.sessionId;if(!n)throw Error(`Session has no session ID. Send a message first.`);let r=createClientUrl(this.#e.host,createEveCancelTurnRoutePath(n)),i=await this.#e.resolveHeaders();i.set(`content-type`,`application/json`);let o=await fetch(r,withRedirectPolicy({headers:i,method:`POST`,body:e?JSON.stringify(e):void 0},this.#e.redirect)),c=await o.text();if(!o.ok)throw new ClientError(o.status,c,o.headers);let u;try{u=JSON.parse(c)}catch{throw Error(`Cancel route returned invalid JSON (${o.status}).`)}let d=CancelTurnResponseSchema.safeParse(u);if(!d.success||d.data.sessionId!==n)throw Error(`Cancel route returned an invalid response (${o.status}).`);return{sessionId:d.data.sessionId,status:d.data.status}}stream(e){let t=this.#t.sessionId;if(!t)throw Error(`Session has no session ID. Send a message first.`);return this.#i(t,e)}async#n(t,i){let a=i.sessionId?createEveContinueSessionRoutePath(i.sessionId):EVE_CREATE_SESSION_ROUTE_PATH,o=createClientUrl(this.#e.host,a),c=await this.#e.resolveHeaders(t.headers);c.set(`content-type`,`application/json`);let l=createHandleMessageBody({input:t,outputSchema:serializeOutputSchema(t.outputSchema),session:i});if(l===null)throw Error(`Session.send requires a non-empty message, inputResponses, or both.`);let u=await postTurnWithRetry({body:JSON.stringify(l),headers:c,mustDeliver:(t.inputResponses?.length??0)>0,redirect:this.#e.redirect,signal:t.signal,url:o}),f=await u.json(),p=(typeof f.sessionId==`string`?f.sessionId:void 0)??u.headers.get(EVE_SESSION_ID_HEADER)?.trim()??i.sessionId;if(!p)throw Error(`Message route did not return a session id.`);return{continuationToken:typeof f.continuationToken==`string`?f.continuationToken:void 0,sessionId:p}}async*#r(e,t,n,r){let a=[];try{for await(let t of followStreamIterable({host:this.#e.host,resolveHeaders:()=>this.#e.resolveHeaders(r.headers),redirect:this.#e.redirect,sessionId:e,signal:r.signal,startIndex:n.sessionId===e?n.streamIndex:0}))if(a.push(t),yield t,isCurrentTurnBoundaryEvent(t))break}finally{this.#t=advanceSession({continuationToken:t,events:a,preserveCompletedSessions:this.#e.preserveCompletedSessions,sessionId:e,session:n})}}async*#i(e,t){let n=this.#t,r=t?.startIndex??n.streamIndex,i=[];try{for await(let n of followStreamIterable({host:this.#e.host,resolveHeaders:()=>this.#e.resolveHeaders(),redirect:this.#e.redirect,sessionId:e,signal:t?.signal,startIndex:r}))i.push(n),yield n}finally{r>=0&&(this.#t=advanceSession({continuationToken:n.continuationToken,events:i,preserveCompletedSessions:this.#e.preserveCompletedSessions,session:{...n,sessionId:e,streamIndex:r},sessionId:e}))}}};async function postTurnWithRetry(e){let t=e.mustDeliver?10:1,n,r,i;for(let o=0;o<t;o+=1){let s=await fetch(e.url,{body:e.body,headers:e.headers,method:`POST`,redirect:e.redirect,signal:e.signal??null});if(s.ok)return s;if(n=s.status,r=await s.text(),i=s.headers,!isRetryableDeliveryFailure(s.status,r))throw new ClientError(s.status,r,s.headers);o<t-1&&await sleep(200)}throw new ClientError(n??0,r??`Failed to deliver session turn.`,i)}function isRetryableDeliveryFailure(e,t){return e===500&&/target session was not found/i.test(t)}async function sleep(e){await new Promise(t=>setTimeout(t,e))}function normalizeSendTurnInput(e){return typeof e==`string`?{message:e}:e}function createHandleMessageBody(e){let t={};return e.input.message!==void 0&&(t.message=e.input.message),e.input.inputResponses!==void 0&&e.input.inputResponses.length>0&&(t.inputResponses=e.input.inputResponses),e.input.clientContext!==void 0&&(t.clientContext=e.input.clientContext),e.outputSchema!==void 0&&(t.outputSchema=e.outputSchema),e.session.continuationToken!==void 0&&(t.continuationToken=e.session.continuationToken),Object.keys(t).length===0||e.session.continuationToken===void 0&&t.message===void 0||e.session.continuationToken!==void 0&&t.message===void 0&&t.inputResponses===void 0?null:t}function withRedirectPolicy(e,t){return t===void 0?e:{...e,redirect:t}}export{ClientSession};
|
|
1
|
+
import{EVE_CREATE_SESSION_ROUTE_PATH,createEveCancelTurnRoutePath,createEveContinueSessionRoutePath}from"#protocol/routes.js";import{EVE_SESSION_ID_HEADER,isCurrentTurnBoundaryEvent}from"#protocol/message.js";import{ClientError}from"#client/client-error.js";import{advanceSession}from"#client/session-utils.js";import{createClientUrl}from"#client/url.js";import{MessageResponse}from"#client/message-response.js";import{CancelTurnResponseSchema}from"#protocol/cancel-turn.js";import{followStreamIterable}from"#client/open-stream.js";import{serializeOutputSchema}from"#shared/tool-schema.js";var ClientSession=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}get state(){return this.#t}async send(e){let t=normalizeSendTurnInput(e),n=this.#t,{continuationToken:r,sessionId:i}=await this.#n(t,n);return this.#t===n&&(this.#t={...n,sessionId:i}),new MessageResponse({continuationToken:r,createStream:()=>this.#r(i,r,n,t),sessionId:i})}async cancel(e){let n=this.#t.sessionId;if(!n)throw Error(`Session has no session ID. Send a message first.`);let r=createClientUrl(this.#e.host,createEveCancelTurnRoutePath(n)),i=await this.#e.resolveHeaders();i.set(`content-type`,`application/json`);let o=await fetch(r,withRedirectPolicy({headers:i,method:`POST`,body:e?JSON.stringify(e):void 0},this.#e.redirect)),c=await o.text();if(!o.ok)throw new ClientError(o.status,c,o.headers);let u;try{u=JSON.parse(c)}catch{throw Error(`Cancel route returned invalid JSON (${o.status}).`)}let d=CancelTurnResponseSchema.safeParse(u);if(!d.success||d.data.sessionId!==n)throw Error(`Cancel route returned an invalid response (${o.status}).`);return{sessionId:d.data.sessionId,status:d.data.status}}stream(e){let t=this.#t.sessionId;if(!t)throw Error(`Session has no session ID. Send a message first.`);return this.#i(t,e)}async#n(t,i){let a=i.sessionId?createEveContinueSessionRoutePath(i.sessionId):EVE_CREATE_SESSION_ROUTE_PATH,o=createClientUrl(this.#e.host,a),c=await this.#e.resolveHeaders(t.headers);c.set(`content-type`,`application/json`);let l=createHandleMessageBody({input:t,outputSchema:serializeOutputSchema(t.outputSchema),session:i});if(l===null)throw Error(`Session.send requires a non-empty message, inputResponses, or both.`);let u=await postTurnWithRetry({body:JSON.stringify(l),headers:c,mustDeliver:(t.inputResponses?.length??0)>0,redirect:this.#e.redirect,signal:t.signal,url:o}),f=await u.json(),p=(typeof f.sessionId==`string`?f.sessionId:void 0)??u.headers.get(EVE_SESSION_ID_HEADER)?.trim()??i.sessionId;if(!p)throw Error(`Message route did not return a session id.`);return{continuationToken:typeof f.continuationToken==`string`?f.continuationToken:void 0,sessionId:p}}async*#r(e,t,n,r){let a=[];try{for await(let t of followStreamIterable({host:this.#e.host,resolveHeaders:()=>this.#e.resolveHeaders(r.headers),redirect:this.#e.redirect,streamReconnectPolicy:r.streamReconnectPolicy,sessionId:e,signal:r.signal,startIndex:n.sessionId===e?n.streamIndex:0}))if(a.push(t),yield t,isCurrentTurnBoundaryEvent(t))break}finally{this.#t=advanceSession({continuationToken:t,events:a,preserveCompletedSessions:this.#e.preserveCompletedSessions,sessionId:e,session:n})}}async*#i(e,t){let n=this.#t,r=t?.startIndex??n.streamIndex,i=[];try{for await(let n of followStreamIterable({host:this.#e.host,resolveHeaders:()=>this.#e.resolveHeaders(),redirect:this.#e.redirect,streamReconnectPolicy:t?.streamReconnectPolicy,sessionId:e,signal:t?.signal,startIndex:r}))i.push(n),yield n}finally{r>=0&&(this.#t=advanceSession({continuationToken:n.continuationToken,events:i,preserveCompletedSessions:this.#e.preserveCompletedSessions,session:{...n,sessionId:e,streamIndex:r},sessionId:e}))}}};async function postTurnWithRetry(e){let t=e.mustDeliver?10:1,n,r,i;for(let o=0;o<t;o+=1){let s=await fetch(e.url,{body:e.body,headers:e.headers,method:`POST`,redirect:e.redirect,signal:e.signal??null});if(s.ok)return s;if(n=s.status,r=await s.text(),i=s.headers,!isRetryableDeliveryFailure(s.status,r))throw new ClientError(s.status,r,s.headers);o<t-1&&await sleep(200)}throw new ClientError(n??0,r??`Failed to deliver session turn.`,i)}function isRetryableDeliveryFailure(e,t){return e===500&&/target session was not found/i.test(t)}async function sleep(e){await new Promise(t=>setTimeout(t,e))}function normalizeSendTurnInput(e){return typeof e==`string`?{message:e}:e}function createHandleMessageBody(e){let t={};return e.input.message!==void 0&&(t.message=e.input.message),e.input.inputResponses!==void 0&&e.input.inputResponses.length>0&&(t.inputResponses=e.input.inputResponses),e.input.clientContext!==void 0&&(t.clientContext=e.input.clientContext),e.outputSchema!==void 0&&(t.outputSchema=e.outputSchema),e.session.continuationToken!==void 0&&(t.continuationToken=e.session.continuationToken),Object.keys(t).length===0||e.session.continuationToken===void 0&&t.message===void 0||e.session.continuationToken!==void 0&&t.message===void 0&&t.inputResponses===void 0?null:t}function withRedirectPolicy(e,t){return t===void 0?e:{...e,redirect:t}}export{ClientSession};
|
|
@@ -118,6 +118,11 @@ export interface SendTurnPayload<TOutput = unknown> {
|
|
|
118
118
|
* schema's output type and is not revalidated client-side.
|
|
119
119
|
*/
|
|
120
120
|
readonly outputSchema?: StandardJSONSchemaV1<unknown, TOutput> | JsonObject;
|
|
121
|
+
/**
|
|
122
|
+
* Reconnection policy for the response event stream. Omit to use the default
|
|
123
|
+
* policy, or pass `{ reconnect: false }` when the caller owns cursor recovery.
|
|
124
|
+
*/
|
|
125
|
+
readonly streamReconnectPolicy?: StreamReconnectPolicy;
|
|
121
126
|
/**
|
|
122
127
|
* Abort signal for cancelling the request.
|
|
123
128
|
*/
|
|
@@ -127,10 +132,37 @@ export interface SendTurnPayload<TOutput = unknown> {
|
|
|
127
132
|
*/
|
|
128
133
|
readonly headers?: Readonly<Record<string, string>>;
|
|
129
134
|
}
|
|
135
|
+
/** Retry and backoff settings for one kind of stream reconnection. */
|
|
136
|
+
export interface StreamReconnectRetryPolicy {
|
|
137
|
+
/** Initial delay before retrying, in milliseconds. */
|
|
138
|
+
readonly baseDelayMs?: number;
|
|
139
|
+
/** Maximum number of attempts governed by this retry policy. */
|
|
140
|
+
readonly maxAttempts?: number;
|
|
141
|
+
/** Maximum delay between retries, in milliseconds. */
|
|
142
|
+
readonly maxDelayMs?: number;
|
|
143
|
+
}
|
|
144
|
+
/** Configurable policy used when automatic stream reconnection is enabled. */
|
|
145
|
+
export interface ResolvedStreamReconnectPolicy {
|
|
146
|
+
/** Retry policy for opening an HTTP stream connection. */
|
|
147
|
+
readonly streamOpenReconnectPolicy?: StreamReconnectRetryPolicy;
|
|
148
|
+
/** Retry policy for reconnecting streams that make no progress. */
|
|
149
|
+
readonly streamIdleReconnectPolicy?: StreamReconnectRetryPolicy;
|
|
150
|
+
/** HTTP response statuses that may be retried while opening a stream. */
|
|
151
|
+
readonly retryableErrorStatuses?: readonly number[];
|
|
152
|
+
}
|
|
153
|
+
/** Automatic stream reconnection configuration. */
|
|
154
|
+
export type StreamReconnectPolicy = ResolvedStreamReconnectPolicy | {
|
|
155
|
+
readonly reconnect: false;
|
|
156
|
+
};
|
|
130
157
|
/**
|
|
131
158
|
* Options for {@link ClientSession.stream}.
|
|
132
159
|
*/
|
|
133
160
|
export interface StreamOptions {
|
|
161
|
+
/**
|
|
162
|
+
* Reconnection policy for the event stream. Omit to use the default policy,
|
|
163
|
+
* or pass `{ reconnect: false }` when the caller owns cursor recovery.
|
|
164
|
+
*/
|
|
165
|
+
readonly streamReconnectPolicy?: StreamReconnectPolicy;
|
|
134
166
|
/**
|
|
135
167
|
* Absolute event index to start from. Negative values read relative to the
|
|
136
168
|
* current tail (`-1` starts at the latest event). Relative-tail streams do
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ModelMessage } from "ai";
|
|
2
2
|
import type { HarnessToolDefinition } from "#harness/execute-tool.js";
|
|
3
|
-
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
3
|
+
import type { HandleMessageStreamEvent, SessionStartedStreamEvent } from "#protocol/message.js";
|
|
4
4
|
import type { ResolvedDynamicToolResolver } from "#runtime/types.js";
|
|
5
5
|
import type { ContextContainer } from "#context/container.js";
|
|
6
6
|
import type { DurableDynamicToolMetadata } from "#context/keys.js";
|
|
@@ -23,3 +23,15 @@ export declare function dispatchDynamicToolEvent(input: {
|
|
|
23
23
|
readonly event: HandleMessageStreamEvent;
|
|
24
24
|
readonly messages: readonly ModelMessage[];
|
|
25
25
|
}): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Re-resolves session-scoped dynamic tools when a durable session reaches a
|
|
28
|
+
* different runtime revision. The refresh is internal: lifecycle consumers
|
|
29
|
+
* still observe exactly one `session.started` event for the session.
|
|
30
|
+
*/
|
|
31
|
+
export declare function refreshDynamicSessionToolsForRuntimeRevision(input: {
|
|
32
|
+
readonly ctx: ContextContainer;
|
|
33
|
+
readonly resolvers: readonly ResolvedDynamicToolResolver[];
|
|
34
|
+
readonly event: SessionStartedStreamEvent;
|
|
35
|
+
readonly messages: readonly ModelMessage[];
|
|
36
|
+
readonly runtimeRevision: string;
|
|
37
|
+
}): Promise<void>;
|