@kortix/sdk 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -73
- package/dist/browser/cache/idb-sync-cache-key.d.ts +2 -0
- package/dist/browser/cache/idb-sync-cache-key.d.ts.map +1 -0
- package/dist/browser/cache/idb-sync-cache-key.js +7 -0
- package/dist/browser/cache/idb-sync-cache-key.js.map +1 -0
- package/dist/browser/cache/idb-sync-cache.d.ts +3 -3
- package/dist/browser/cache/idb-sync-cache.d.ts.map +1 -1
- package/dist/browser/cache/idb-sync-cache.js +49 -26
- package/dist/browser/cache/idb-sync-cache.js.map +1 -1
- package/dist/browser/session-sync/session-cache-ownership.d.ts +36 -0
- package/dist/browser/session-sync/session-cache-ownership.d.ts.map +1 -0
- package/dist/browser/session-sync/session-cache-ownership.js +66 -0
- package/dist/browser/session-sync/session-cache-ownership.js.map +1 -0
- package/dist/browser/session-sync/session-sync-registry.d.ts +8 -6
- package/dist/browser/session-sync/session-sync-registry.d.ts.map +1 -1
- package/dist/browser/session-sync/session-sync-registry.js +86 -33
- package/dist/browser/session-sync/session-sync-registry.js.map +1 -1
- package/dist/browser/session-sync/session-transcript-cache.d.ts +55 -0
- package/dist/browser/session-sync/session-transcript-cache.d.ts.map +1 -0
- package/dist/browser/session-sync/session-transcript-cache.js +69 -0
- package/dist/browser/session-sync/session-transcript-cache.js.map +1 -0
- package/dist/core/client/kortix.d.ts +72 -25
- package/dist/core/client/kortix.d.ts.map +1 -1
- package/dist/core/client/kortix.js +76 -17
- package/dist/core/client/kortix.js.map +1 -1
- package/dist/core/rest/platform-client/host-boundary.d.ts +7 -0
- package/dist/core/rest/platform-client/host-boundary.d.ts.map +1 -1
- package/dist/core/rest/platform-client/host-boundary.js +14 -0
- package/dist/core/rest/platform-client/host-boundary.js.map +1 -1
- package/dist/core/rest/projects-client/access.d.ts +1 -2
- package/dist/core/rest/projects-client/access.d.ts.map +1 -1
- package/dist/core/rest/projects-client/access.js +9 -6
- package/dist/core/rest/projects-client/access.js.map +1 -1
- package/dist/core/rest/projects-client/agent-config.d.ts +15 -7
- package/dist/core/rest/projects-client/agent-config.d.ts.map +1 -1
- package/dist/core/rest/projects-client/agent-config.js +43 -2
- package/dist/core/rest/projects-client/agent-config.js.map +1 -1
- package/dist/core/rest/projects-client/agent-scope.d.ts +4 -0
- package/dist/core/rest/projects-client/agent-scope.d.ts.map +1 -1
- package/dist/core/rest/projects-client/agent-scope.js +34 -1
- package/dist/core/rest/projects-client/agent-scope.js.map +1 -1
- package/dist/core/rest/projects-client/audit.d.ts +34 -0
- package/dist/core/rest/projects-client/audit.d.ts.map +1 -1
- package/dist/core/rest/projects-client/audit.js +49 -4
- package/dist/core/rest/projects-client/audit.js.map +1 -1
- package/dist/core/rest/projects-client/billing.d.ts +26 -14
- package/dist/core/rest/projects-client/billing.d.ts.map +1 -1
- package/dist/core/rest/projects-client/billing.js +4 -5
- package/dist/core/rest/projects-client/billing.js.map +1 -1
- package/dist/core/rest/projects-client/connectors.d.ts +106 -45
- package/dist/core/rest/projects-client/connectors.d.ts.map +1 -1
- package/dist/core/rest/projects-client/connectors.js +95 -58
- package/dist/core/rest/projects-client/connectors.js.map +1 -1
- package/dist/core/rest/projects-client/github.d.ts +7 -1
- package/dist/core/rest/projects-client/github.d.ts.map +1 -1
- package/dist/core/rest/projects-client/github.js.map +1 -1
- package/dist/core/rest/projects-client/iam.d.ts +17 -0
- package/dist/core/rest/projects-client/iam.d.ts.map +1 -1
- package/dist/core/rest/projects-client/iam.js +14 -0
- package/dist/core/rest/projects-client/iam.js.map +1 -1
- package/dist/core/rest/projects-client/index.d.ts +2 -0
- package/dist/core/rest/projects-client/index.d.ts.map +1 -1
- package/dist/core/rest/projects-client/index.js +2 -0
- package/dist/core/rest/projects-client/index.js.map +1 -1
- package/dist/core/rest/projects-client/model-enablement.d.ts +9 -0
- package/dist/core/rest/projects-client/model-enablement.d.ts.map +1 -0
- package/dist/core/rest/projects-client/model-enablement.js +16 -0
- package/dist/core/rest/projects-client/model-enablement.js.map +1 -0
- package/dist/core/rest/projects-client/policies.d.ts +18 -0
- package/dist/core/rest/projects-client/policies.d.ts.map +1 -1
- package/dist/core/rest/projects-client/policies.js.map +1 -1
- package/dist/core/rest/projects-client/projects.d.ts +87 -1
- package/dist/core/rest/projects-client/projects.d.ts.map +1 -1
- package/dist/core/rest/projects-client/projects.js +8 -0
- package/dist/core/rest/projects-client/projects.js.map +1 -1
- package/dist/core/rest/projects-client/sandbox.d.ts +50 -0
- package/dist/core/rest/projects-client/sandbox.d.ts.map +1 -1
- package/dist/core/rest/projects-client/sandbox.js.map +1 -1
- package/dist/core/rest/projects-client/session-costs.d.ts +102 -0
- package/dist/core/rest/projects-client/session-costs.d.ts.map +1 -0
- package/dist/core/rest/projects-client/session-costs.js +25 -0
- package/dist/core/rest/projects-client/session-costs.js.map +1 -0
- package/dist/core/rest/projects-client/session-sandbox.d.ts +0 -2
- package/dist/core/rest/projects-client/session-sandbox.d.ts.map +1 -1
- package/dist/core/rest/projects-client/session-sandbox.js +1 -10
- package/dist/core/rest/projects-client/session-sandbox.js.map +1 -1
- package/dist/core/rest/projects-client/sessions.d.ts +94 -44
- package/dist/core/rest/projects-client/sessions.d.ts.map +1 -1
- package/dist/core/rest/projects-client/sessions.js +9 -15
- package/dist/core/rest/projects-client/sessions.js.map +1 -1
- package/dist/core/rest/projects-client/setup-links.d.ts +37 -0
- package/dist/core/rest/projects-client/setup-links.d.ts.map +1 -1
- package/dist/core/rest/projects-client/setup-links.js +11 -0
- package/dist/core/rest/projects-client/setup-links.js.map +1 -1
- package/dist/core/session/index.d.ts +1 -1
- package/dist/core/session/index.d.ts.map +1 -1
- package/dist/core/session/index.js +1 -1
- package/dist/core/session/index.js.map +1 -1
- package/dist/core/session/preview-auth.d.ts +32 -0
- package/dist/core/session/preview-auth.d.ts.map +1 -0
- package/dist/core/session/preview-auth.js +53 -0
- package/dist/core/session/preview-auth.js.map +1 -0
- package/dist/core/session/server-store/url-helpers.d.ts.map +1 -1
- package/dist/core/session/server-store/url-helpers.js +5 -3
- package/dist/core/session/server-store/url-helpers.js.map +1 -1
- package/dist/core/session-sync/session-sync-controller.d.ts +12 -1
- package/dist/core/session-sync/session-sync-controller.d.ts.map +1 -1
- package/dist/core/session-sync/session-sync-controller.js +34 -2
- package/dist/core/session-sync/session-sync-controller.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/kortix.esm.min.js +19 -24
- package/dist/kortix.global.js +19 -24
- package/dist/platform/auth-core.d.ts +5 -3
- package/dist/platform/auth-core.d.ts.map +1 -1
- package/dist/platform/auth-core.js +6 -4
- package/dist/platform/auth-core.js.map +1 -1
- package/dist/react/index.d.ts +1 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +0 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/model-flatten.d.ts +20 -0
- package/dist/react/model-flatten.d.ts.map +1 -1
- package/dist/react/model-flatten.js +12 -0
- package/dist/react/model-flatten.js.map +1 -1
- package/dist/react/opencode.d.ts +4 -3
- package/dist/react/opencode.d.ts.map +1 -1
- package/dist/react/opencode.js +4 -3
- package/dist/react/opencode.js.map +1 -1
- package/dist/react/provider-selection.d.ts +9 -0
- package/dist/react/provider-selection.d.ts.map +1 -1
- package/dist/react/provider-selection.js +23 -0
- package/dist/react/provider-selection.js.map +1 -1
- package/dist/react/session-title-sync.d.ts +9 -5
- package/dist/react/session-title-sync.d.ts.map +1 -1
- package/dist/react/session-title-sync.js +14 -10
- package/dist/react/session-title-sync.js.map +1 -1
- package/dist/react/use-canonical-opencode-session.d.ts +2 -2
- package/dist/react/use-canonical-opencode-session.d.ts.map +1 -1
- package/dist/react/use-model-enablement.d.ts +25 -0
- package/dist/react/use-model-enablement.d.ts.map +1 -0
- package/dist/react/use-model-enablement.js +92 -0
- package/dist/react/use-model-enablement.js.map +1 -0
- package/dist/react/use-model-store.d.ts +6 -14
- package/dist/react/use-model-store.d.ts.map +1 -1
- package/dist/react/use-model-store.js +30 -52
- package/dist/react/use-model-store.js.map +1 -1
- package/dist/react/use-opencode-events/handle-event.d.ts.map +1 -1
- package/dist/react/use-opencode-events/handle-event.js +103 -41
- package/dist/react/use-opencode-events/handle-event.js.map +1 -1
- package/dist/react/use-opencode-local.d.ts +5 -4
- package/dist/react/use-opencode-local.d.ts.map +1 -1
- package/dist/react/use-opencode-local.js +20 -37
- package/dist/react/use-opencode-local.js.map +1 -1
- package/dist/react/use-opencode-sessions/keys.d.ts +3 -1
- package/dist/react/use-opencode-sessions/keys.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/keys.js +2 -0
- package/dist/react/use-opencode-sessions/keys.js.map +1 -1
- package/dist/react/use-opencode-sessions/sessions.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/sessions.js +16 -7
- package/dist/react/use-opencode-sessions/sessions.js.map +1 -1
- package/dist/react/use-opencode-sessions/shared.d.ts +3 -2
- package/dist/react/use-opencode-sessions/shared.d.ts.map +1 -1
- package/dist/react/use-opencode-sessions/shared.js +6 -3
- package/dist/react/use-opencode-sessions/shared.js.map +1 -1
- package/dist/react/use-opencode-sessions/sharing.js +2 -2
- package/dist/react/use-opencode-sessions/sharing.js.map +1 -1
- package/dist/react/use-session-sync.d.ts +13 -1
- package/dist/react/use-session-sync.d.ts.map +1 -1
- package/dist/react/use-session-sync.js +85 -17
- package/dist/react/use-session-sync.js.map +1 -1
- package/dist/react/use-session.d.ts +60 -40
- package/dist/react/use-session.d.ts.map +1 -1
- package/dist/react/use-session.js +160 -202
- package/dist/react/use-session.js.map +1 -1
- package/package.json +2 -2
- package/dist/core/acp/client.d.ts +0 -45
- package/dist/core/acp/client.d.ts.map +0 -1
- package/dist/core/acp/client.js +0 -277
- package/dist/core/acp/client.js.map +0 -1
- package/dist/core/acp/index.d.ts +0 -5
- package/dist/core/acp/index.d.ts.map +0 -1
- package/dist/core/acp/index.js +0 -5
- package/dist/core/acp/index.js.map +0 -1
- package/dist/core/acp/projection.d.ts +0 -23
- package/dist/core/acp/projection.d.ts.map +0 -1
- package/dist/core/acp/projection.js +0 -603
- package/dist/core/acp/projection.js.map +0 -1
- package/dist/core/acp/session-controller.d.ts +0 -90
- package/dist/core/acp/session-controller.d.ts.map +0 -1
- package/dist/core/acp/session-controller.js +0 -530
- package/dist/core/acp/session-controller.js.map +0 -1
- package/dist/core/acp/types.d.ts +0 -66
- package/dist/core/acp/types.d.ts.map +0 -1
- package/dist/core/acp/types.js +0 -21
- package/dist/core/acp/types.js.map +0 -1
- package/dist/core/session/runtime-transport.d.ts +0 -16
- package/dist/core/session/runtime-transport.d.ts.map +0 -1
- package/dist/core/session/runtime-transport.js +0 -30
- package/dist/core/session/runtime-transport.js.map +0 -1
- package/dist/react/session-busy.d.ts +0 -8
- package/dist/react/session-busy.d.ts.map +0 -1
- package/dist/react/session-busy.js +0 -4
- package/dist/react/session-busy.js.map +0 -1
- package/dist/react/use-acp-session-runtime.d.ts +0 -34
- package/dist/react/use-acp-session-runtime.d.ts.map +0 -1
- package/dist/react/use-acp-session-runtime.js +0 -84
- package/dist/react/use-acp-session-runtime.js.map +0 -1
- package/dist/react/use-admin-users.d.ts +0 -98
- package/dist/react/use-admin-users.d.ts.map +0 -1
- package/dist/react/use-admin-users.js +0 -121
- package/dist/react/use-admin-users.js.map +0 -1
|
@@ -21,10 +21,9 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
|
21
21
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
22
22
|
import { getClient, RuntimeNotReadyError } from '../core/runtime/client.js';
|
|
23
23
|
import { commitSessionRewind, messagesBeforeRewind, reconcileCommittedSessionRewind, } from '../core/session/rewind.js';
|
|
24
|
-
import { createSessionRuntimePolicy, } from '../core/session/runtime-transport.js';
|
|
25
24
|
import { useOpenCodeCompactionStore } from '../browser/stores/opencode-compaction-store.js';
|
|
26
25
|
import { useOpenCodePendingStore } from '../browser/stores/opencode-pending-store.js';
|
|
27
|
-
import { setOpenCodeHealth, setSandboxStatus } from '../browser/stores/sandbox-connection-store.js';
|
|
26
|
+
import { setOpenCodeHealth, setSandboxStatus, } from '../browser/stores/sandbox-connection-store.js';
|
|
28
27
|
import { getSandboxUrlForExternalId } from '../browser/stores/server-store.js';
|
|
29
28
|
import { useSyncStore } from '../browser/stores/sync-store.js';
|
|
30
29
|
import { beginSessionPromptObservation, endSessionPromptObservation, } from '../browser/session-sync/session-sync-registry.js';
|
|
@@ -35,8 +34,6 @@ import { BillingError, parseBillingError } from '../core/http/api/errors.js';
|
|
|
35
34
|
import { formatOpenCodeRuntimeError } from '../core/http/opencode-errors.js';
|
|
36
35
|
import { extractGatewayErrorDetails } from '../core/turns/errors.js';
|
|
37
36
|
import { useCanonicalOpenCodeSession } from './use-canonical-opencode-session.js';
|
|
38
|
-
import { useAcpSessionRuntime } from './use-acp-session-runtime.js';
|
|
39
|
-
import { resolveSessionBusy } from './session-busy.js';
|
|
40
37
|
import { useOpenCodeEventStream } from './use-opencode-events/index.js';
|
|
41
38
|
import { formatModelString } from './use-opencode-local.js';
|
|
42
39
|
import { useProjectConfig } from './use-project-config.js';
|
|
@@ -47,7 +44,7 @@ import { useRuntimePhase } from './use-runtime-phase.js';
|
|
|
47
44
|
import { clearStartStash, readStartStash } from './session-start-stash.js';
|
|
48
45
|
import { useSessionPicks } from './use-session-picks.js';
|
|
49
46
|
import { useSessionSync } from './use-session-sync.js';
|
|
50
|
-
import {
|
|
47
|
+
import { reconcileHydratedSessionTitle } from './session-title-sync.js';
|
|
51
48
|
import { useVisibleAgents } from './use-visible-agents.js';
|
|
52
49
|
import { rejectQuestion as rejectQuestionApi, replyToPermission, replyToQuestion, useAbortOpenCodeSession, useExecuteOpenCodeCommand, useSendOpenCodeMessage, } from './use-opencode-sessions/index.js';
|
|
53
50
|
import { unwrap } from './use-opencode-sessions/shared.js';
|
|
@@ -69,12 +66,93 @@ export function shouldRetrySessionStart(failureCount, error, sessionId) {
|
|
|
69
66
|
}
|
|
70
67
|
return !isSessionStartError(error) && failureCount < 3;
|
|
71
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Gap between `/start` polls. The server long-polls each tick, so this is the
|
|
71
|
+
* pause between holds, not the latency to observe `ready`.
|
|
72
|
+
*/
|
|
73
|
+
export const SESSION_START_POLL_MS = 1_500;
|
|
74
|
+
/**
|
|
75
|
+
* Should the `/start` boot poll fire again, given the last tick's outcome?
|
|
76
|
+
* `false` = stop: a terminal stage, or a terminal client error that no amount
|
|
77
|
+
* of polling can fix. Everything else keeps polling — including a `null`
|
|
78
|
+
* payload from a transient transport failure, since the box is still coming up.
|
|
79
|
+
*/
|
|
80
|
+
export function shouldPollSessionStart(error, data) {
|
|
81
|
+
if (isSessionStartError(error))
|
|
82
|
+
return false;
|
|
83
|
+
const stage = data?.stage;
|
|
84
|
+
return stage === 'ready' || stage === 'failed' || stage === 'stopped'
|
|
85
|
+
? false
|
|
86
|
+
: SESSION_START_POLL_MS;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* TanStack Query pauses interval fetches while the document is hidden unless
|
|
90
|
+
* this option is true. Session readiness must continue because it gates the
|
|
91
|
+
* runtime switch, event stream, and queued-prompt replay.
|
|
92
|
+
*/
|
|
93
|
+
export const SESSION_START_POLL_OPTIONS = {
|
|
94
|
+
refetchInterval: (query) => shouldPollSessionStart(query.state.error, query.state.data),
|
|
95
|
+
refetchIntervalInBackground: true,
|
|
96
|
+
};
|
|
72
97
|
// `getClient()` (packages/sdk/src/opencode/client.ts) throws a
|
|
73
98
|
// `RuntimeNotReadyError` with this exact message when the sandbox url hasn't
|
|
74
99
|
// been resolved yet (session still starting). The string match is kept as a
|
|
75
100
|
// fallback for callers that re-wrap the original error (losing the
|
|
76
101
|
// `instanceof` chain) but still preserve its message.
|
|
77
102
|
const RUNTIME_NOT_READY_MARKER = 'Server URL not ready';
|
|
103
|
+
/** The error body, wherever the transport parked it. */
|
|
104
|
+
function errorBody(error) {
|
|
105
|
+
if (!error || typeof error !== 'object')
|
|
106
|
+
return null;
|
|
107
|
+
const holder = error;
|
|
108
|
+
for (const key of ['data', 'details', 'body']) {
|
|
109
|
+
const candidate = holder[key];
|
|
110
|
+
if (candidate && typeof candidate === 'object')
|
|
111
|
+
return candidate;
|
|
112
|
+
}
|
|
113
|
+
return holder;
|
|
114
|
+
}
|
|
115
|
+
function errorBodyMessage(error) {
|
|
116
|
+
const body = errorBody(error);
|
|
117
|
+
for (const key of ['message', 'error']) {
|
|
118
|
+
const value = body?.[key];
|
|
119
|
+
if (typeof value === 'string' && value.trim())
|
|
120
|
+
return value.trim();
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The connector profiles a `CONNECTOR_AUTHORIZATION_REQUIRED` refusal names, or
|
|
126
|
+
* null when this is not that refusal.
|
|
127
|
+
*
|
|
128
|
+
* Keyed on the CODE, never on the message: the prose is for humans and changes.
|
|
129
|
+
* A refusal that names no profile still returns null — a connector prompt that
|
|
130
|
+
* cannot say which connector is worse than the generic error it replaced.
|
|
131
|
+
*/
|
|
132
|
+
function connectorRefusalProfiles(error) {
|
|
133
|
+
const body = errorBody(error);
|
|
134
|
+
if (body?.code !== 'CONNECTOR_AUTHORIZATION_REQUIRED')
|
|
135
|
+
return null;
|
|
136
|
+
const listed = [body.connector_profiles, body.connectorProfiles].find(Array.isArray);
|
|
137
|
+
const profiles = [];
|
|
138
|
+
for (const entry of listed ?? []) {
|
|
139
|
+
if (!entry || typeof entry !== 'object')
|
|
140
|
+
continue;
|
|
141
|
+
const profile = entry;
|
|
142
|
+
const strategy = profile.authorization_strategy ?? profile.authorizationStrategy;
|
|
143
|
+
if (typeof profile.id !== 'string' || typeof profile.slug !== 'string')
|
|
144
|
+
continue;
|
|
145
|
+
if (strategy !== 'project' && strategy !== 'user')
|
|
146
|
+
continue;
|
|
147
|
+
profiles.push({
|
|
148
|
+
id: profile.id,
|
|
149
|
+
slug: profile.slug,
|
|
150
|
+
name: typeof profile.name === 'string' && profile.name ? profile.name : profile.slug,
|
|
151
|
+
authorization_strategy: strategy,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return profiles.length > 0 ? profiles : null;
|
|
155
|
+
}
|
|
78
156
|
/** Classify a thrown/rejected error from a send or a permission/question reply
|
|
79
157
|
* into a `KortixSendError`. Pure — safe to unit test without a runtime. */
|
|
80
158
|
export function classifySendError(error) {
|
|
@@ -89,10 +167,15 @@ export function classifySendError(error) {
|
|
|
89
167
|
if (error && typeof error === 'object') {
|
|
90
168
|
const parsed = parseBillingError(error);
|
|
91
169
|
if (parsed instanceof BillingError) {
|
|
170
|
+
return { kind: 'billing', message: parsed.message, billing: parsed, cause: error };
|
|
171
|
+
}
|
|
172
|
+
const connectors = connectorRefusalProfiles(error);
|
|
173
|
+
if (connectors) {
|
|
92
174
|
return {
|
|
93
|
-
kind: '
|
|
94
|
-
message:
|
|
95
|
-
|
|
175
|
+
kind: 'connector',
|
|
176
|
+
message: errorBodyMessage(error) ??
|
|
177
|
+
'Connect the required connector profiles before continuing this session.',
|
|
178
|
+
connectors,
|
|
96
179
|
cause: error,
|
|
97
180
|
};
|
|
98
181
|
}
|
|
@@ -138,25 +221,24 @@ export function sendStateOnStart(text) {
|
|
|
138
221
|
export function sendStateOnError(error) {
|
|
139
222
|
return { pending: null, sendError: classifySendError(error) };
|
|
140
223
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
* `promptAsync()` accepts a prompt.
|
|
144
|
-
*
|
|
145
|
-
* The runtime can accept the prompt before the browser receives its first SSE
|
|
146
|
-
* status event. Without this optimistic busy state, the 10-second liveness
|
|
147
|
-
* reconciliation never starts. The transcript can then stay at the user
|
|
148
|
-
* message even though the agent completed the turn.
|
|
149
|
-
*/
|
|
150
|
-
export function beginRestPromptObservation(sessionId) {
|
|
151
|
-
beginSessionPromptObservation(sessionId);
|
|
224
|
+
export function beginRestPromptObservation(sessionId, runtimeScope) {
|
|
225
|
+
beginSessionPromptObservation(sessionId, runtimeScope);
|
|
152
226
|
useSyncStore.getState().setStatus(sessionId, { type: 'busy' });
|
|
153
227
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
endSessionPromptObservation(sessionId);
|
|
228
|
+
export function endRestPromptObservation(sessionId, runtimeScope) {
|
|
229
|
+
endSessionPromptObservation(sessionId, runtimeScope);
|
|
157
230
|
useSyncStore.getState().setStatus(sessionId, { type: 'idle' });
|
|
158
231
|
}
|
|
159
|
-
|
|
232
|
+
export async function sendRestPromptWithObservation(sessionId, runtimeScope, sendPrompt) {
|
|
233
|
+
beginRestPromptObservation(sessionId, runtimeScope);
|
|
234
|
+
try {
|
|
235
|
+
await sendPrompt();
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
endRestPromptObservation(sessionId, runtimeScope);
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
160
242
|
export async function rewindOpenCodeSession(sessionId, messageId) {
|
|
161
243
|
if (!messageId)
|
|
162
244
|
throw new Error('Session rewind requires a message id');
|
|
@@ -165,7 +247,6 @@ export async function rewindOpenCodeSession(sessionId, messageId) {
|
|
|
165
247
|
messageID: messageId,
|
|
166
248
|
}));
|
|
167
249
|
}
|
|
168
|
-
/** Restore a staged OpenCode rollback before a replacement prompt commits it. */
|
|
169
250
|
export async function restoreOpenCodeSessionRewind(sessionId) {
|
|
170
251
|
unwrap(await getClient().session.unrevert({ sessionID: sessionId }));
|
|
171
252
|
}
|
|
@@ -228,7 +309,7 @@ const DISABLED_CHAT_ENGINE_SYNC = {
|
|
|
228
309
|
export function useSession(projectId, sessionId, options = {}) {
|
|
229
310
|
const queryClient = useQueryClient();
|
|
230
311
|
const titleRefreshAbortRef = useRef(null);
|
|
231
|
-
const { waitMs = 15_000, replayStartStash = true, enabled = true, chatEngine = true, initialOpenCodeSessionId = null,
|
|
312
|
+
const { waitMs = 15_000, replayStartStash = true, enabled = true, chatEngine = true, initialOpenCodeSessionId = null, } = options;
|
|
232
313
|
// 1. Drive /start until the runtime is ready (the server long-polls each tick).
|
|
233
314
|
const start = useQuery({
|
|
234
315
|
queryKey: sessionStartKey(projectId, sessionId),
|
|
@@ -238,12 +319,7 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
238
319
|
retryDelay: (failureCount, error) => isSessionStartError(error) && error.status === 404
|
|
239
320
|
? FRESH_START_404_RETRY_DELAY_MS
|
|
240
321
|
: Math.min(1000 * 2 ** failureCount, 5000),
|
|
241
|
-
|
|
242
|
-
if (isSessionStartError(q.state.error))
|
|
243
|
-
return false;
|
|
244
|
-
const stage = q.state.data?.stage;
|
|
245
|
-
return stage === 'ready' || stage === 'failed' || stage === 'stopped' ? false : 1500;
|
|
246
|
-
},
|
|
322
|
+
...SESSION_START_POLL_OPTIONS,
|
|
247
323
|
});
|
|
248
324
|
const startData = start.data ?? null;
|
|
249
325
|
const startError = isSessionStartError(start.error) ? start.error : null;
|
|
@@ -251,9 +327,6 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
251
327
|
const sandbox = startData?.sandbox ?? null;
|
|
252
328
|
const startReady = stage === 'ready';
|
|
253
329
|
const terminal = stage === 'failed' || stage === 'stopped';
|
|
254
|
-
const runtimePolicy = createSessionRuntimePolicy(startData?.runtime_transport, runtimeTransportOverride);
|
|
255
|
-
const runtimeTransport = runtimePolicy.transport;
|
|
256
|
-
const usesAcp = runtimePolicy.useAcp;
|
|
257
330
|
// 2. Point the SDK's runtime at this session's sandbox once ready. Track WHICH
|
|
258
331
|
// sandbox we switched to (not a bare bool) so navigating between sessions (this
|
|
259
332
|
// hook instance is reused) re-gates instead of binding the new session to the
|
|
@@ -287,7 +360,7 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
287
360
|
// 4. Open the live SSE stream. This was a provider component (OpenCodeEvent
|
|
288
361
|
// StreamProvider); calling the underlying hook here means the host mounts
|
|
289
362
|
// nothing. It self-gates on the connection store's healthy flag (seeded above).
|
|
290
|
-
useOpenCodeEventStream({ enabled:
|
|
363
|
+
useOpenCodeEventStream({ enabled: switched });
|
|
291
364
|
// 5. Resolve the canonical OpenCode root id (server-owned; /start hands it over)
|
|
292
365
|
// and sync messages off it.
|
|
293
366
|
const canonicalSession = useCanonicalOpenCodeSession({
|
|
@@ -295,22 +368,10 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
295
368
|
sessionId,
|
|
296
369
|
pinFromStart: startData?.opencode_session_id ?? null,
|
|
297
370
|
initialPin: initialOpenCodeSessionId,
|
|
298
|
-
listRuntimeSessions:
|
|
371
|
+
listRuntimeSessions: switched,
|
|
299
372
|
});
|
|
300
373
|
const { rootSessionId } = canonicalSession;
|
|
301
374
|
const ocSessionId = rootSessionId ?? '';
|
|
302
|
-
const runtimeUrl = sandbox?.external_id ? getSandboxUrlForExternalId(sandbox.external_id) : null;
|
|
303
|
-
const acpRuntime = useAcpSessionRuntime({
|
|
304
|
-
runtimeUrl,
|
|
305
|
-
sessionId: rootSessionId,
|
|
306
|
-
enabled: enabled && switched && usesAcp,
|
|
307
|
-
});
|
|
308
|
-
const acpSessionTitle = readAcpSessionTitle(acpRuntime.projection.sessionInfo);
|
|
309
|
-
useEffect(() => {
|
|
310
|
-
if (!usesAcp || !acpSessionTitle)
|
|
311
|
-
return;
|
|
312
|
-
syncAcpSessionTitleQuery(queryClient, projectId, sessionId, { title: acpSessionTitle });
|
|
313
|
-
}, [queryClient, projectId, sessionId, usesAcp, acpSessionTitle]);
|
|
314
375
|
useEffect(() => () => {
|
|
315
376
|
titleRefreshAbortRef.current?.abort();
|
|
316
377
|
titleRefreshAbortRef.current = null;
|
|
@@ -320,29 +381,17 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
320
381
|
// is off — `useSessionSync('')` fetches/polls nothing (its effects no-op on
|
|
321
382
|
// a falsy/non-canonical session id) — and use a fixed, type-stable empty
|
|
322
383
|
// result instead of whatever it happens to return for that starved call.
|
|
323
|
-
const rawSync = useSessionSync(chatEngine
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
isLoading: !acpRuntime.ready,
|
|
329
|
-
diffs: [],
|
|
330
|
-
todos: acpRuntime.projection.todos,
|
|
331
|
-
hasOlder: false,
|
|
332
|
-
isLoadingOlder: false,
|
|
333
|
-
loadOlder: async () => { },
|
|
334
|
-
}), [acpRuntime.projection, acpRuntime.ready]);
|
|
335
|
-
const sync = !chatEngine ? DISABLED_CHAT_ENGINE_SYNC : usesAcp ? acpSync : rawSync;
|
|
384
|
+
const rawSync = useSessionSync(chatEngine ? ocSessionId : '', {
|
|
385
|
+
kortixSessionScope: `${projectId}/${sessionId}`,
|
|
386
|
+
networkEnabled: switched,
|
|
387
|
+
});
|
|
388
|
+
const sync = chatEngine ? rawSync : DISABLED_CHAT_ENGINE_SYNC;
|
|
336
389
|
const runtimePhase = useRuntimePhase();
|
|
337
390
|
const [restRewind, setRestRewind] = useState(null);
|
|
338
391
|
const [rewindPending, setRewindPending] = useState(false);
|
|
339
392
|
const [rewindError, setRewindError] = useState(null);
|
|
340
|
-
const rewindMessageId =
|
|
341
|
-
|
|
342
|
-
: restRewind?.staged
|
|
343
|
-
? restRewind.messageId
|
|
344
|
-
: null;
|
|
345
|
-
const messages = useMemo(() => usesAcp ? sync.messages : messagesBeforeRewind(sync.messages, restRewind?.messageId ?? null), [usesAcp, sync.messages, restRewind?.messageId]);
|
|
393
|
+
const rewindMessageId = restRewind?.staged ? restRewind.messageId : null;
|
|
394
|
+
const messages = useMemo(() => messagesBeforeRewind(sync.messages, restRewind?.messageId ?? null), [sync.messages, restRewind?.messageId]);
|
|
346
395
|
useEffect(() => {
|
|
347
396
|
setRestRewind(null);
|
|
348
397
|
setRewindPending(false);
|
|
@@ -350,7 +399,7 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
350
399
|
}, [sessionId, ocSessionId]);
|
|
351
400
|
useEffect(() => {
|
|
352
401
|
setRestRewind((current) => reconcileCommittedSessionRewind(sync.messages, current));
|
|
353
|
-
}, [
|
|
402
|
+
}, [sync.messages]);
|
|
354
403
|
// 5b. Self-heal a missed `question.asked` SSE event (a `question` tool part
|
|
355
404
|
// rendering as running with nothing in the pending store) — see
|
|
356
405
|
// `useQuestionSelfHeal` for why this is distinct from the SSE reconnect-gap
|
|
@@ -358,24 +407,21 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
358
407
|
// is off — see that option's jsdoc: a host mounting its own chat surface
|
|
359
408
|
// already runs its own copy of this poller for the same session.
|
|
360
409
|
useQuestionSelfHeal(ocSessionId, sync.messages, {
|
|
361
|
-
enabled:
|
|
410
|
+
enabled: switched && chatEngine && !!ocSessionId,
|
|
362
411
|
});
|
|
363
412
|
usePermissionSelfHeal(ocSessionId, sync.messages, {
|
|
364
|
-
enabled:
|
|
413
|
+
enabled: switched && chatEngine && !!ocSessionId,
|
|
365
414
|
});
|
|
366
415
|
// 6. Interactive prompts live in the pending store (the SSE writes them there,
|
|
367
416
|
// keyed by request id carrying sessionID). useSessionSync does NOT surface them.
|
|
368
417
|
const questionMap = useOpenCodePendingStore((s) => s.questions);
|
|
369
418
|
const permissionMap = useOpenCodePendingStore((s) => s.permissions);
|
|
370
|
-
const isCompacting = useOpenCodeCompactionStore((state) =>
|
|
419
|
+
const isCompacting = useOpenCodeCompactionStore((state) => switched && Boolean(state.compactingBySession[ocSessionId]));
|
|
371
420
|
const removeQuestion = useOpenCodePendingStore((s) => s.removeQuestion);
|
|
372
421
|
const removePermission = useOpenCodePendingStore((s) => s.removePermission);
|
|
373
|
-
const questions = useMemo(() =>
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const permissions = useMemo(() => usesAcp
|
|
377
|
-
? acpRuntime.projection.permissions
|
|
378
|
-
: Object.values(permissionMap).filter((p) => p.sessionID === ocSessionId), [usesAcp, acpRuntime.projection.permissions, permissionMap, ocSessionId]);
|
|
422
|
+
const questions = useMemo(() => switched ? Object.values(questionMap).filter((q) => q.sessionID === ocSessionId) : [], [questionMap, ocSessionId, switched]);
|
|
423
|
+
const permissions = useMemo(() => switched ? Object.values(permissionMap).filter((p) => p.sessionID === ocSessionId) : [], [permissionMap, ocSessionId, switched]);
|
|
424
|
+
const runtimeActionReady = switched && !!rootSessionId;
|
|
379
425
|
// 7. Server-side capabilities + per-session picks (all pre-runtime — no sandbox).
|
|
380
426
|
const models = useProjectModels(projectId);
|
|
381
427
|
const agents = useVisibleAgents({ projectId });
|
|
@@ -389,6 +435,21 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
389
435
|
// lands (count grows) — robust to server-normalized text where a text-equality
|
|
390
436
|
// match would clear too early or never (wedging the composer). 30s backstop.
|
|
391
437
|
const userMsgCount = useMemo(() => messages.filter((m) => m.info.role === 'user').length, [messages]);
|
|
438
|
+
useEffect(() => {
|
|
439
|
+
if (!chatEngine || userMsgCount <= 0)
|
|
440
|
+
return;
|
|
441
|
+
titleRefreshAbortRef.current?.abort();
|
|
442
|
+
const controller = new AbortController();
|
|
443
|
+
titleRefreshAbortRef.current = controller;
|
|
444
|
+
void reconcileHydratedSessionTitle(queryClient, projectId, sessionId, userMsgCount, {
|
|
445
|
+
signal: controller.signal,
|
|
446
|
+
}).finally(() => {
|
|
447
|
+
if (titleRefreshAbortRef.current === controller) {
|
|
448
|
+
titleRefreshAbortRef.current = null;
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
return () => controller.abort();
|
|
452
|
+
}, [chatEngine, projectId, queryClient, sessionId, userMsgCount]);
|
|
392
453
|
const [sendState, setSendState] = useState(IDLE_SEND_STATE);
|
|
393
454
|
const pending = sendState.pending;
|
|
394
455
|
const pendingBaseCount = useRef(0);
|
|
@@ -404,82 +465,41 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
404
465
|
return () => clearTimeout(t);
|
|
405
466
|
}, [pending]);
|
|
406
467
|
const sendParts = async (parts, override) => {
|
|
407
|
-
if (!
|
|
468
|
+
if (!runtimeActionReady)
|
|
408
469
|
throw new RuntimeNotReadyError();
|
|
409
|
-
titleRefreshAbortRef.current?.abort();
|
|
410
|
-
const titleRefreshAbort = new AbortController();
|
|
411
|
-
titleRefreshAbortRef.current = titleRefreshAbort;
|
|
412
|
-
void refreshSessionTitleQueryUntilResolved(queryClient, projectId, sessionId, {
|
|
413
|
-
signal: titleRefreshAbort.signal,
|
|
414
|
-
}).finally(() => {
|
|
415
|
-
if (titleRefreshAbortRef.current === titleRefreshAbort) {
|
|
416
|
-
titleRefreshAbortRef.current = null;
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
470
|
const model = override?.model ?? picks.model;
|
|
420
471
|
const agent = override?.agent ?? picks.agent;
|
|
421
472
|
const variant = override?.variant;
|
|
422
|
-
if (usesAcp) {
|
|
423
|
-
const acpParts = parts.map((part) => {
|
|
424
|
-
if (part.type === 'text') {
|
|
425
|
-
return { type: 'text', text: part.text };
|
|
426
|
-
}
|
|
427
|
-
if (part.type === 'agent') {
|
|
428
|
-
return { type: 'text', text: `@${part.name}` };
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
type: 'resource_link',
|
|
432
|
-
uri: part.url,
|
|
433
|
-
name: part.filename,
|
|
434
|
-
mimeType: part.mime,
|
|
435
|
-
};
|
|
436
|
-
});
|
|
437
|
-
await acpRuntime.send(acpParts, {
|
|
438
|
-
model: model ? formatModelString(model) : null,
|
|
439
|
-
agent,
|
|
440
|
-
});
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
473
|
const opts = {
|
|
444
474
|
...(model ? { model } : {}),
|
|
445
475
|
...(agent ? { agent } : {}),
|
|
446
476
|
...(variant ? { variant } : {}),
|
|
447
477
|
...(override?.directory ? { directory: override.directory } : {}),
|
|
448
478
|
};
|
|
449
|
-
await sendMutation.mutateAsync({
|
|
479
|
+
await sendRestPromptWithObservation(ocSessionId, sandbox?.external_id ?? undefined, () => sendMutation.mutateAsync({
|
|
450
480
|
sessionId: ocSessionId,
|
|
451
481
|
parts,
|
|
452
482
|
...(Object.keys(opts).length ? { options: opts } : {}),
|
|
453
|
-
});
|
|
483
|
+
}));
|
|
454
484
|
setRestRewind(commitSessionRewind);
|
|
455
485
|
};
|
|
456
486
|
const send = (text, override) => {
|
|
457
|
-
if (!
|
|
487
|
+
if (!runtimeActionReady)
|
|
458
488
|
return;
|
|
459
489
|
pendingBaseCount.current = userMsgCount;
|
|
460
|
-
if (!usesAcp)
|
|
461
|
-
beginRestPromptObservation(ocSessionId);
|
|
462
490
|
setSendState(sendStateOnStart(text));
|
|
463
491
|
void sendParts([{ type: 'text', text }], override).catch((error) => {
|
|
464
|
-
if (!usesAcp)
|
|
465
|
-
endRestPromptObservation(ocSessionId);
|
|
466
492
|
setSendState(sendStateOnError(error));
|
|
467
493
|
});
|
|
468
494
|
};
|
|
469
495
|
// Run a project slash-command (server-side `/command`), distinct from a prompt.
|
|
470
496
|
const runCommand = (command, args, options = {}) => {
|
|
471
|
-
if (!
|
|
497
|
+
if (!runtimeActionReady)
|
|
472
498
|
return Promise.resolve();
|
|
473
|
-
if (usesAcp) {
|
|
474
|
-
return acpRuntime.runCommand(command, args, {
|
|
475
|
-
model: options.model ? formatModelString(options.model) : null,
|
|
476
|
-
agent: options.agent,
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
499
|
return commandMutation.mutateAsync(buildSessionCommandInput(ocSessionId, command, args, options));
|
|
480
500
|
};
|
|
481
501
|
const rewind = async (messageId) => {
|
|
482
|
-
if (!
|
|
502
|
+
if (!runtimeActionReady)
|
|
483
503
|
throw new RuntimeNotReadyError();
|
|
484
504
|
if (!messageId)
|
|
485
505
|
throw new Error('Session rewind requires a message id');
|
|
@@ -489,13 +509,8 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
489
509
|
setRewindPending(true);
|
|
490
510
|
setRewindError(null);
|
|
491
511
|
try {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
await rewindOpenCodeSession(ocSessionId, messageId);
|
|
497
|
-
setRestRewind({ messageId, staged: true });
|
|
498
|
-
}
|
|
512
|
+
await rewindOpenCodeSession(ocSessionId, messageId);
|
|
513
|
+
setRestRewind({ messageId, staged: true });
|
|
499
514
|
}
|
|
500
515
|
catch (error) {
|
|
501
516
|
const classified = classifySendError(error);
|
|
@@ -507,20 +522,15 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
507
522
|
}
|
|
508
523
|
};
|
|
509
524
|
const restoreRewind = async () => {
|
|
510
|
-
if (!
|
|
525
|
+
if (!runtimeActionReady)
|
|
511
526
|
throw new RuntimeNotReadyError();
|
|
512
527
|
if (!rewindMessageId || rewindPending)
|
|
513
528
|
return;
|
|
514
529
|
setRewindPending(true);
|
|
515
530
|
setRewindError(null);
|
|
516
531
|
try {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
else {
|
|
521
|
-
await restoreOpenCodeSessionRewind(ocSessionId);
|
|
522
|
-
setRestRewind(null);
|
|
523
|
-
}
|
|
532
|
+
await restoreOpenCodeSessionRewind(ocSessionId);
|
|
533
|
+
setRestRewind(null);
|
|
524
534
|
}
|
|
525
535
|
catch (error) {
|
|
526
536
|
const classified = classifySendError(error);
|
|
@@ -533,49 +543,15 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
533
543
|
};
|
|
534
544
|
// The one true cancel: abort the run AND drop any pending prompt + open prompts.
|
|
535
545
|
const cancel = () => {
|
|
536
|
-
if (
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
else {
|
|
540
|
-
endRestPromptObservation(ocSessionId);
|
|
541
|
-
abortMutation.mutate(ocSessionId);
|
|
542
|
-
}
|
|
546
|
+
if (runtimeActionReady) {
|
|
547
|
+
endRestPromptObservation(ocSessionId);
|
|
548
|
+
abortMutation.mutate(ocSessionId);
|
|
543
549
|
}
|
|
544
550
|
questions.forEach((q) => removeQuestion(q.id));
|
|
545
551
|
permissions.forEach((p) => removePermission(p.id));
|
|
546
552
|
setSendState(IDLE_SEND_STATE);
|
|
547
553
|
};
|
|
548
|
-
const
|
|
549
|
-
if (!usesAcp)
|
|
550
|
-
return answerQuestion(requestId, answers);
|
|
551
|
-
try {
|
|
552
|
-
await acpRuntime.answerQuestion(requestId, answers);
|
|
553
|
-
}
|
|
554
|
-
catch (error) {
|
|
555
|
-
throw classifySendError(error);
|
|
556
|
-
}
|
|
557
|
-
};
|
|
558
|
-
const rejectSessionQuestion = async (requestId) => {
|
|
559
|
-
if (!usesAcp)
|
|
560
|
-
return rejectQuestion(requestId);
|
|
561
|
-
try {
|
|
562
|
-
await acpRuntime.rejectQuestion(requestId);
|
|
563
|
-
}
|
|
564
|
-
catch (error) {
|
|
565
|
-
throw classifySendError(error);
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
const answerSessionPermission = async (requestId, reply, message) => {
|
|
569
|
-
if (!usesAcp)
|
|
570
|
-
return answerPermission(requestId, reply, message);
|
|
571
|
-
try {
|
|
572
|
-
await acpRuntime.answerPermission(requestId, reply);
|
|
573
|
-
}
|
|
574
|
-
catch (error) {
|
|
575
|
-
throw classifySendError(error);
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
const runtimeSessionError = usesAcp ? acpRuntime.error : canonicalSession.error;
|
|
554
|
+
const runtimeSessionError = canonicalSession.error;
|
|
579
555
|
const phase = terminal || startError || runtimeSessionError ? 'error' : switched ? 'ready' : 'starting';
|
|
580
556
|
// 10. Replay the new-session hand-off once ready + thread empty (exactly once).
|
|
581
557
|
// Force-disabled when `chatEngine` is off: this reads `sync.isLoading`/
|
|
@@ -599,11 +575,7 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
599
575
|
picks.setModel(stash.model);
|
|
600
576
|
if (stash.agent)
|
|
601
577
|
picks.setAgent(stash.agent);
|
|
602
|
-
send(stash.prompt, {
|
|
603
|
-
model: stash.model,
|
|
604
|
-
agent: stash.agent,
|
|
605
|
-
variant: stash.variant,
|
|
606
|
-
});
|
|
578
|
+
send(stash.prompt, { model: stash.model, agent: stash.agent, variant: stash.variant });
|
|
607
579
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
608
580
|
}, [phase, sync.isLoading, sync.messages.length, sessionId, replayStartStash, chatEngine]);
|
|
609
581
|
return {
|
|
@@ -611,10 +583,7 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
611
583
|
sessionId,
|
|
612
584
|
/** Canonical OpenCode root id, or null while resolving. */
|
|
613
585
|
opencodeSessionId: rootSessionId ?? null,
|
|
614
|
-
|
|
615
|
-
runtimeTransport,
|
|
616
|
-
/** Runtime sessions available for legacy deep-link selection.
|
|
617
|
-
* ACP exposes only the canonical session during this canary. */
|
|
586
|
+
runtimeTransport: 'rest',
|
|
618
587
|
runtimeSessions: canonicalSession.sessions,
|
|
619
588
|
runtimeSessionsLoading: canonicalSession.isLoading,
|
|
620
589
|
runtimeSessionsListed: canonicalSession.listed,
|
|
@@ -643,12 +612,7 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
643
612
|
startError,
|
|
644
613
|
/** Granular boot phase (connecting|booting|ready|unreachable) for detailed UI. */
|
|
645
614
|
runtimePhase,
|
|
646
|
-
isBusy:
|
|
647
|
-
syncBusy: sync.isBusy,
|
|
648
|
-
hasPendingText: !!pending,
|
|
649
|
-
usesAcp,
|
|
650
|
-
acpSending: acpRuntime.sending,
|
|
651
|
-
}),
|
|
615
|
+
isBusy: sync.isBusy || !!pending,
|
|
652
616
|
isCompacting,
|
|
653
617
|
isLoading: sync.isLoading,
|
|
654
618
|
isError: terminal || !!startError || !!runtimeSessionError,
|
|
@@ -659,15 +623,12 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
659
623
|
/** Pending optimistic message text, or null. */
|
|
660
624
|
pending,
|
|
661
625
|
/** True while the current `send` mutation is in flight. */
|
|
662
|
-
isSending:
|
|
626
|
+
isSending: sendMutation.isPending,
|
|
663
627
|
/** Last `send` failure, typed (billing / runtime-not-ready / runtime-error),
|
|
664
628
|
* or null. Reset on every new `send` call. */
|
|
665
629
|
sendError: sendState.sendError,
|
|
666
|
-
/** Selected user message while a reversible history rewind is staged. */
|
|
667
630
|
rewindMessageId,
|
|
668
|
-
/** True while the runtime stages or restores a history rewind. */
|
|
669
631
|
rewindPending,
|
|
670
|
-
/** Last history rewind failure, or null. */
|
|
671
632
|
rewindError,
|
|
672
633
|
// server-side capabilities (pre-runtime)
|
|
673
634
|
models,
|
|
@@ -677,22 +638,19 @@ export function useSession(projectId, sessionId, options = {}) {
|
|
|
677
638
|
picks,
|
|
678
639
|
// actions
|
|
679
640
|
send,
|
|
680
|
-
/** Send text and file prompt parts through the selected SDK transport. */
|
|
681
641
|
sendParts,
|
|
682
|
-
/** Rewind this canonical session to one user message. */
|
|
683
642
|
rewind,
|
|
684
|
-
/** Restore the removed path before a replacement prompt commits it. */
|
|
685
643
|
restoreRewind,
|
|
686
644
|
cancel,
|
|
687
645
|
runCommand,
|
|
688
646
|
/** Answer an agent question through the server and drop it from pending
|
|
689
647
|
* state on success; throws a `KortixSendError` and leaves it pending on
|
|
690
648
|
* failure. */
|
|
691
|
-
answerQuestion
|
|
649
|
+
answerQuestion,
|
|
692
650
|
/** Reject an agent question through the server (see `answerQuestion`). */
|
|
693
|
-
rejectQuestion
|
|
651
|
+
rejectQuestion,
|
|
694
652
|
/** Answer an agent permission request through the server (see `answerQuestion`). */
|
|
695
|
-
answerPermission
|
|
653
|
+
answerPermission,
|
|
696
654
|
/** @deprecated Drops the question from local state WITHOUT replying to the
|
|
697
655
|
* server — the agent run stays blocked waiting on it. Use `answerQuestion`
|
|
698
656
|
* / `rejectQuestion` instead. */
|