@ai-matrx/chat 0.4.10 → 0.4.14
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 +74 -0
- package/dist/agents/components/context-items/bodies/ContextInputBody.js +1 -1
- package/dist/agents/components/context-items/bodies/GenericBody.js +1 -1
- package/dist/agents/components/conversation-actions/conversation-verbs.js +1 -1
- package/dist/agents/components/inputs/smart-input/RunSkillPicker.js +1 -1
- package/dist/agents/components/live-run/LiveRunDisplay.js +1 -1
- package/dist/agents/components/live-run/LiveRunProgress.js +1 -1
- package/dist/agents/components/live-run/RunSetDisplay.js +1 -1
- package/dist/agents/components/messages-display/assistant/AgentAssistantMessage.js +2 -2
- package/dist/agents/components/messages-display/assistant/InPlaceAnswerEditor.js +1 -1
- package/dist/agents/components/messages-display/conversation-tools/find-in-conversation.js +1 -1
- package/dist/agents/components/messages-display/message-options/buildTaskSeedFromMessage.js +1 -1
- package/dist/agents/components/messages-display/user/AgentUserMessage.js +1 -1
- package/dist/agents/components/messages-display/user/FirstTurnVariables.js +1 -1
- package/dist/agents/components/run/AgentStatusIndicator.js +1 -1
- package/dist/agents/components/run/RunJobWorkingLine.js +1 -1
- package/dist/agents/components/shared/AgentNewRunButton.js +1 -1
- package/dist/agents/components/shared/AgentPlanningIndicator.js +1 -1
- package/dist/agents/conversation-export/conversation-transfer.js +1 -1
- package/dist/agents/conversation-export/document-markdown.js +1 -1
- package/dist/agents/hooks/useConversationRoutePromotion.js +1 -1
- package/dist/agents/hooks/useModelControls.js +1 -1
- package/dist/agents/redux/agent-definition/converters.js +1 -1
- package/dist/agents/redux/agent-definition/slice.js +1 -1
- package/dist/agents/redux/conversation-list/conversation-list.thunks.js +1 -1
- package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +2 -2
- package/dist/agents/redux/execution-system/active-requests/active-requests.slice.d.ts +2 -0
- package/dist/agents/redux/execution-system/active-requests/active-requests.slice.js +7 -0
- package/dist/agents/redux/execution-system/conversations/conversation-persistence.js +2 -4
- package/dist/agents/redux/execution-system/instance-context/sync-canvas-item-context.thunk.js +1 -1
- package/dist/agents/redux/execution-system/thunks/dispatch-matrx-extend-tool.thunk.js +3 -1
- package/dist/agents/redux/execution-system/thunks/process-stream.js +27 -24
- package/dist/agents/redux/execution-system/thunks/run-ai-stream.d.ts +7 -0
- package/dist/agents/redux/execution-system/thunks/run-ai-stream.js +13 -0
- package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +1 -1
- package/dist/agents/redux/execution-system/utils/assemble-cx-content-blocks.js +1 -1
- package/dist/agents/redux/execution-system/utils/normalize-content-blocks.js +1 -1
- package/dist/agents/redux/execution-system/utils/output-contract-guard.js +1 -1
- package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +8 -8
- package/dist/agents/runtime-reconnect/api.d.ts +6 -25
- package/dist/agents/runtime-reconnect/api.js +7 -13
- package/dist/agents/runtime-reconnect/reconnect-server-operation.thunk.js +167 -159
- package/dist/agents/runtime-reconnect/types.d.ts +2 -0
- package/dist/agents/services/mcp-connections.service.d.ts +12 -0
- package/dist/agents/services/mcp-connections.service.js +4 -7
- package/dist/agents/types/request.types.d.ts +6 -0
- package/dist/agents/utils/canvasItemContext.js +1 -1
- package/dist/agents/utils/json-extraction.js +2 -2
- package/dist/canvas/workspace/ChatPanelTitleMenu.js +1 -1
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +1 -1
- package/dist/cx-chat/components/SsrSidebarChats.js +1 -1
- package/dist/cx-chat/components/core/ChatConversationClient.js +1 -1
- package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
- package/dist/cx-chat/hooks/useInstanceBootstrap.js +1 -1
- package/dist/cx-conversation/AssistantMessage.js +1 -1
- package/dist/host/configure.js +15 -5
- package/dist/host/contract.d.ts +43 -2
- package/dist/host/db.js +6 -0
- package/dist/host/defaults/diagnostics.d.ts +5 -0
- package/dist/host/defaults/diagnostics.js +1 -2
- package/dist/host/defaults/identity.d.ts +26 -0
- package/dist/host/defaults/identity.js +133 -1
- package/dist/host/identity.d.ts +19 -2
- package/dist/host/identity.js +18 -10
- package/dist/host/session-retry.js +2 -2
- package/dist/mandates/llm-params.d.ts +1 -1
- package/dist/mandates/output-contract.js +1 -1
- package/dist/mandates/service.d.ts +1 -1
- package/dist/mandates/service.js +1 -1
- package/dist/mandates/test-run-core.js +1 -1
- package/dist/public-chat/components/PublicMessageOptionsMenu.js +1 -1
- package/dist/surfaces/services/bind-agent-to-surface.service.js +2 -4
- package/dist/surfaces/services/surface-config.service.js +1 -1
- package/dist/tool-call-visualization/components/ToolCallBatch.js +1 -1
- package/dist/tool-call-visualization/components/ToolCallVisualization.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/AgentCallInline.js +1 -1
- package/dist/tool-call-visualization/renderers/agent-call/CollabCallCard.js +2 -2
- package/dist/tool-call-visualization/renderers/agent-call/collab-subtitle.js +3 -3
- package/dist/tool-call-visualization/renderers/skill/SkillInline.js +1 -1
- package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +1 -1
- package/dist/tool-call-visualization/result-fields/ResultValue.js +1 -1
- package/dist/tool-call-visualization/result-fields/shape.js +1 -1
- package/dist/tool-call-visualization/utils/cxToolCallToLifecycleEntry.js +2 -2
- package/dist/tool-call-visualization/utils/human-copy.js +3 -3
- package/dist/ui/mandates-storage/provision-shapes.d.ts +1 -1
- package/dist/ui/mandates-storage/provision-shapes.js +1 -1
- package/dist/ui/markdown-stream/EnhancedChatMarkdown.js +1 -1
- package/dist/ui/treatment-shape.d.ts +1 -1
- package/dist/ui/treatment-shape.js +1 -1
- package/dist/voice-agent/relay/useVoiceRelaySession.js +1 -1
- package/package.json +24 -6
- package/dist/agents/hooks/useWarmAgent.d.ts +0 -8
- package/dist/agents/hooks/useWarmAgent.js +0 -25
- package/dist/agents/utils/artifactId.d.ts +0 -34
- package/dist/agents/utils/artifactId.js +0 -15
- package/dist/ui/ShimmerText.d.ts +0 -5
- package/dist/ui/ShimmerText.js +0 -21
- package/dist/ui/addressWithoutNavigating.d.ts +0 -68
- package/dist/ui/addressWithoutNavigating.js +0 -60
- package/dist/utils/content-ir/decision-answers-ids.d.ts +0 -3
- package/dist/utils/content-ir/decision-answers-ids.js +0 -6
- package/dist/utils/content-ir/envelope-parity.d.ts +0 -12
- package/dist/utils/content-ir/envelope-parity.js +0 -32
- package/dist/utils/content-ir/instance-title.d.ts +0 -26
- package/dist/utils/content-ir/instance-title.js +0 -39
- package/dist/utils/content-ir/kinds/kind-markdown-utils.d.ts +0 -84
- package/dist/utils/content-ir/kinds/kind-markdown-utils.js +0 -233
- package/dist/utils/content-ir/kinds/kind-value-markdown.d.ts +0 -5
- package/dist/utils/content-ir/kinds/kind-value-markdown.js +0 -17
- package/dist/utils/content-ir/legacy-quiz-envelope.d.ts +0 -3
- package/dist/utils/content-ir/legacy-quiz-envelope.js +0 -45
- package/dist/utils/content-ir/surfaces/embedded-kind-json.d.ts +0 -140
- package/dist/utils/content-ir/surfaces/embedded-kind-json.js +0 -527
- package/dist/utils/content-ir/surfaces/json-kind-signal.d.ts +0 -307
- package/dist/utils/content-ir/surfaces/json-kind-signal.js +0 -945
- package/dist/utils/content-ir/surfaces/kind-image-alt.d.ts +0 -27
- package/dist/utils/content-ir/surfaces/kind-image-alt.js +0 -117
- package/dist/utils/content-ir/surfaces/kind-one-line.d.ts +0 -67
- package/dist/utils/content-ir/surfaces/kind-one-line.js +0 -209
- package/dist/utils/content-ir/surfaces/kind-text-label.d.ts +0 -20
- package/dist/utils/content-ir/surfaces/kind-text-label.js +0 -50
- package/dist/utils/content-ir/surfaces/kind-text-to-markdown.d.ts +0 -47
- package/dist/utils/content-ir/surfaces/kind-text-to-markdown.js +0 -399
- package/dist/utils/content-ir/surfaces/markdown-escaped-kind.d.ts +0 -54
- package/dist/utils/content-ir/surfaces/markdown-escaped-kind.js +0 -128
- package/dist/utils/content-ir/surfaces/quoted-kind-lift.d.ts +0 -61
- package/dist/utils/content-ir/surfaces/quoted-kind-lift.js +0 -215
- package/dist/utils/inline-decision/decision-options.d.ts +0 -21
- package/dist/utils/inline-decision/decision-options.js +0 -32
- package/dist/utils/json.d.ts +0 -63
- package/dist/utils/json.js +0 -22
- package/dist/utils/markdown/plain-title.d.ts +0 -25
- package/dist/utils/markdown/plain-title.js +0 -51
- package/dist/utils/rendered-find.d.ts +0 -9
- package/dist/utils/rendered-find.js +0 -68
- package/dist/utils/rich-editor/core/caret-context.d.ts +0 -24
- package/dist/utils/rich-editor/core/caret-context.js +0 -73
- package/dist/utils/rich-editor/in-place/caret-handoff.d.ts +0 -3
- package/dist/utils/rich-editor/in-place/caret-handoff.js +0 -15
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { parseDecisionOptionsFromBody } from "
|
|
2
|
-
import { QuotedKindLift } from "
|
|
3
|
-
import { canonicalizeCompletedLegacyQuizEnvelope } from "
|
|
4
|
-
import { envelopeMatchesParsedSource } from "
|
|
5
|
-
import { MarkdownEscapedKindJson } from "
|
|
6
|
-
import { KindImageAltUnwrap } from "
|
|
1
|
+
import { parseDecisionOptionsFromBody } from "@ai-matrx/rich-content/utils/decision-options";
|
|
2
|
+
import { QuotedKindLift } from "@ai-matrx/content-ir/surfaces";
|
|
3
|
+
import { canonicalizeCompletedLegacyQuizEnvelope } from "@ai-matrx/content-ir/surfaces";
|
|
4
|
+
import { envelopeMatchesParsedSource } from "@ai-matrx/content-ir/surfaces";
|
|
5
|
+
import { MarkdownEscapedKindJson } from "@ai-matrx/content-ir/surfaces";
|
|
6
|
+
import { KindImageAltUnwrap } from "@ai-matrx/content-ir/surfaces";
|
|
7
7
|
import { FENCE_META_KEY, splitFenceInfo } from "@ai-matrx/content-ir/source";
|
|
8
8
|
import {
|
|
9
9
|
hasUnclosedBacktickRun,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
kindGrammar,
|
|
17
17
|
kindObjectProseBreak,
|
|
18
18
|
bareRegionBreaksIntoProse
|
|
19
|
-
} from "
|
|
19
|
+
} from "@ai-matrx/content-ir/surfaces";
|
|
20
20
|
import {
|
|
21
21
|
findBalancedXmlClose,
|
|
22
22
|
initialXmlBalance
|
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
import {
|
|
69
69
|
normalizeRecoveredContainerPiece,
|
|
70
70
|
splitAroundEmbeddedKindJson
|
|
71
|
-
} from "
|
|
71
|
+
} from "@ai-matrx/content-ir/surfaces";
|
|
72
72
|
import { captureError } from "../../../../host/diagnostics.js";
|
|
73
73
|
const isSpecialCodeLanguage = (language) => SPECIAL_CODE_LANGUAGES.includes(language);
|
|
74
74
|
const envelopeForCompletedXmlRegion = (tag, regionText) => hostSplitterEnvelopes()?.xmlRegion(tag, regionText) ?? null;
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
* follower may retry for minutes across a deploy drain by design), and no
|
|
19
19
|
* org header (org rides the conversation body only, like `runAiStream`).
|
|
20
20
|
*/
|
|
21
|
+
import { type FollowServerJobOptions, type ServerJobOutcome } from "@ai-matrx/agents/matrx";
|
|
21
22
|
import type { ResolvedBackend } from "../redux/execution-system/thunks/resolve-base-url.js";
|
|
22
|
-
import type {
|
|
23
|
+
import type { RuntimeOperationsByLinkResponse } from "./types.js";
|
|
23
24
|
/**
|
|
24
25
|
* `GET /runtime/operations/by-link/conversation/{conversationId}` — the
|
|
25
26
|
* identify step. Returns `null` when the surface is absent or the caller owns
|
|
@@ -27,29 +28,9 @@ import type { RuntimeExecutionStatus, RuntimeOperationEvent, RuntimeOperationsBy
|
|
|
27
28
|
* shape by design); throws on other failures.
|
|
28
29
|
*/
|
|
29
30
|
export declare function fetchOperationsByLink(backend: ResolvedBackend, conversationId: string, signal?: AbortSignal): Promise<RuntimeOperationsByLinkResponse | null>;
|
|
30
|
-
export interface FollowOperationResult {
|
|
31
|
-
/** True when the server sent the terminal `end` frame. */
|
|
32
|
-
ended: boolean;
|
|
33
|
-
/** The root status carried on the `end` frame (when `ended`). */
|
|
34
|
-
status: RuntimeExecutionStatus | null;
|
|
35
|
-
}
|
|
36
|
-
export interface FollowOperationOptions {
|
|
37
|
-
backend: ResolvedBackend;
|
|
38
|
-
executionId: string;
|
|
39
|
-
/** Resume cursor — the operation view's `last_event_seq` (0 = from start). */
|
|
40
|
-
lastEventSeq: number;
|
|
41
|
-
/** Caller teardown — aborting resolves the promise with `ended: false`. */
|
|
42
|
-
signal: AbortSignal;
|
|
43
|
-
/** Fired per durable spine event (lifecycle transitions + notes). */
|
|
44
|
-
onEvent: (event: RuntimeOperationEvent, seq: number | null) => void;
|
|
45
|
-
}
|
|
46
31
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* (
|
|
50
|
-
* `end` frame (the operation settled); `{ended: false}` when the caller
|
|
51
|
-
* aborted or every reconnect attempt failed. A WAITING_INPUT park keeps the
|
|
52
|
-
* stream open by design — a resume re-attaches to the same execution and its
|
|
53
|
-
* events continue arriving here on the same cursor.
|
|
32
|
+
* Identify → rejoin → follow → settle for one conversation, on the shared
|
|
33
|
+
* `followServerJob` core over this backend's channel. Chat supplies only its
|
|
34
|
+
* wiring (the Redux rejoin consumer, banner callbacks, `loadConversation`).
|
|
54
35
|
*/
|
|
55
|
-
export declare function
|
|
36
|
+
export declare function followConversationServerJob(backend: ResolvedBackend, conversationId: string, options: Omit<FollowServerJobOptions, "target">): Promise<ServerJobOutcome>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMatrxTransport,
|
|
3
|
-
|
|
3
|
+
followServerJob,
|
|
4
4
|
getRuntimeOperationsByLink
|
|
5
5
|
} from "@ai-matrx/agents/matrx";
|
|
6
6
|
function transportFor(backend) {
|
|
@@ -21,19 +21,13 @@ async function fetchOperationsByLink(backend, conversationId, signal) {
|
|
|
21
21
|
{ limit: 5, ...signal ? { signal } : {} }
|
|
22
22
|
);
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
lastEventSeq: opts.lastEventSeq,
|
|
30
|
-
signal: opts.signal,
|
|
31
|
-
onEvent: (event, seq) => opts.onEvent(event, seq)
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
return { ended: result.ended, status: result.status };
|
|
24
|
+
function followConversationServerJob(backend, conversationId, options) {
|
|
25
|
+
return followServerJob(transportFor(backend), {
|
|
26
|
+
...options,
|
|
27
|
+
target: { linkKind: "conversation", linkId: conversationId }
|
|
28
|
+
});
|
|
35
29
|
}
|
|
36
30
|
export {
|
|
37
31
|
fetchOperationsByLink,
|
|
38
|
-
|
|
32
|
+
followConversationServerJob
|
|
39
33
|
};
|
|
@@ -2,9 +2,7 @@ import { retryReconnect } from "./retry-reconnect.js";
|
|
|
2
2
|
import { discardRetainedTransportConsumer } from "../redux/execution-system/thunks/process-stream.js";
|
|
3
3
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
runtimeOperationRejoinPath,
|
|
7
|
-
settleRunPickup
|
|
5
|
+
runtimeOperationRejoinPath
|
|
8
6
|
} from "@ai-matrx/agents/matrx";
|
|
9
7
|
import { toast } from "../../host/notify.js";
|
|
10
8
|
import { captureError } from "../../host/diagnostics.js";
|
|
@@ -26,7 +24,7 @@ import { resolveBackendForConversation } from "../redux/execution-system/thunks/
|
|
|
26
24
|
import { surfaceColdPendingCalls } from "../redux/execution-system/thunks/surface-cold-pending-calls.thunk.js";
|
|
27
25
|
import { resumeInstance } from "../redux/execution-system/thunks/resume-instance.thunk.js";
|
|
28
26
|
import { selectActivePendingAsksForConversation } from "../ui-first-tools/redux/pending-asks.slice.js";
|
|
29
|
-
import {
|
|
27
|
+
import { followConversationServerJob } from "./api.js";
|
|
30
28
|
import { decideAfterContinueRefused, decideWaitingInputRecovery } from "./waiting-input-recovery.js";
|
|
31
29
|
import { countOpenAsksForConversation } from "./parked-on-person.js";
|
|
32
30
|
const RUNNING_KINDS = /* @__PURE__ */ new Set(["started", "resumed"]);
|
|
@@ -69,56 +67,25 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
69
67
|
followers.delete(conversationId);
|
|
70
68
|
};
|
|
71
69
|
let op = null;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return noFollow;
|
|
83
|
-
}
|
|
84
|
-
const picked = pickServerJobOperation(byLink?.operations);
|
|
85
|
-
op = picked && !picked.is_terminal ? picked : null;
|
|
86
|
-
settledWhileDisconnected = Boolean(picked?.is_terminal);
|
|
87
|
-
} catch (err) {
|
|
88
|
-
const superseded = ctrl.signal.aborted || !stillOwner();
|
|
89
|
-
clearFollower();
|
|
90
|
-
if (superseded) return noFollow;
|
|
91
|
-
console.warn(
|
|
92
|
-
"[runtime-reconnect] operations-by-link fetch failed \u2014 falling back to legacy recovery.",
|
|
93
|
-
{ conversationId, err }
|
|
94
|
-
);
|
|
95
|
-
if (source === "stream-loss") fallbackToLegacyRecovery();
|
|
96
|
-
return noFollow;
|
|
97
|
-
}
|
|
98
|
-
if (!op) {
|
|
99
|
-
clearFollower();
|
|
100
|
-
if (source === "stream-loss") fallbackToLegacyRecovery();
|
|
101
|
-
else if (settledWhileDisconnected)
|
|
102
|
-
await dispatch(loadConversation({ conversationId })).unwrap();
|
|
103
|
-
return noFollow;
|
|
104
|
-
}
|
|
105
|
-
console.warn(
|
|
106
|
-
"[runtime-reconnect] non-terminal server operation found \u2014 following its event stream.",
|
|
107
|
-
{
|
|
108
|
-
conversationId,
|
|
109
|
-
executionId: op.execution_id,
|
|
110
|
-
status: op.status,
|
|
111
|
-
source
|
|
112
|
-
}
|
|
113
|
-
);
|
|
70
|
+
const liveOp = () => {
|
|
71
|
+
if (!op) throw new Error("reconnect: no server operation identified yet");
|
|
72
|
+
return op;
|
|
73
|
+
};
|
|
74
|
+
let identified = false;
|
|
75
|
+
let superseded = false;
|
|
76
|
+
let sawLive = false;
|
|
77
|
+
let rejoinRan = false;
|
|
78
|
+
let standDown = false;
|
|
79
|
+
let waitingRecoveryInFlight = false;
|
|
114
80
|
const stampOperation = (status, waitingInput, recoveryState) => {
|
|
81
|
+
const current = liveOp();
|
|
115
82
|
const serverOperation = {
|
|
116
|
-
executionId:
|
|
117
|
-
userRequestId:
|
|
83
|
+
executionId: current.execution_id,
|
|
84
|
+
userRequestId: current.request_id,
|
|
118
85
|
status,
|
|
119
86
|
waitingInput,
|
|
120
87
|
...recoveryState ? { recoveryState } : {},
|
|
121
|
-
startedAt:
|
|
88
|
+
startedAt: current.started_at,
|
|
122
89
|
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
123
90
|
};
|
|
124
91
|
dispatch(patchConversation({ conversationId, serverOperation }));
|
|
@@ -131,10 +98,10 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
131
98
|
}
|
|
132
99
|
dispatch(setInstanceStatus({ conversationId, status: "paused" }));
|
|
133
100
|
};
|
|
134
|
-
let waitingRecoveryInFlight = false;
|
|
135
101
|
const recoverWaitingInput = async () => {
|
|
136
102
|
if (waitingRecoveryInFlight) return;
|
|
137
103
|
waitingRecoveryInFlight = true;
|
|
104
|
+
const waiting = liveOp();
|
|
138
105
|
stampOperation("waiting_input", true, "checking_for_prompt");
|
|
139
106
|
try {
|
|
140
107
|
const surfaced = await dispatch(
|
|
@@ -148,7 +115,7 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
148
115
|
} catch (err) {
|
|
149
116
|
console.error(
|
|
150
117
|
"[runtime-reconnect] could not read the person's open asks; not resuming a turn that may be waiting on one.",
|
|
151
|
-
{ conversationId, executionId:
|
|
118
|
+
{ conversationId, executionId: waiting.execution_id, err }
|
|
152
119
|
);
|
|
153
120
|
stampOperation("waiting_input", true, "checking_for_prompt");
|
|
154
121
|
return;
|
|
@@ -158,7 +125,7 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
158
125
|
pendingCallCount: surfaced,
|
|
159
126
|
pendingAskCount: asks.length,
|
|
160
127
|
parkedOnPersonCount,
|
|
161
|
-
userRequestId:
|
|
128
|
+
userRequestId: waiting.request_id
|
|
162
129
|
});
|
|
163
130
|
if (decision === "prompt_visible") {
|
|
164
131
|
stampOperation("waiting_input", true, "prompt_visible");
|
|
@@ -175,12 +142,12 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
175
142
|
if (decision === "needs_action") {
|
|
176
143
|
console.error(
|
|
177
144
|
"[runtime-reconnect] waiting operation has no pending prompt and no request_id; user action is required.",
|
|
178
|
-
{ conversationId, executionId:
|
|
145
|
+
{ conversationId, executionId: waiting.execution_id }
|
|
179
146
|
);
|
|
180
147
|
stampOperation("waiting_input", true, "needs_action");
|
|
181
148
|
return;
|
|
182
149
|
}
|
|
183
|
-
const userRequestId =
|
|
150
|
+
const userRequestId = waiting.request_id;
|
|
184
151
|
if (!userRequestId) {
|
|
185
152
|
stampOperation("waiting_input", true, "needs_action");
|
|
186
153
|
return;
|
|
@@ -189,7 +156,7 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
189
156
|
"[runtime-reconnect] waiting operation has zero pending calls \u2014 continuing the resolved request.",
|
|
190
157
|
{
|
|
191
158
|
conversationId,
|
|
192
|
-
executionId:
|
|
159
|
+
executionId: waiting.execution_id,
|
|
193
160
|
userRequestId
|
|
194
161
|
}
|
|
195
162
|
);
|
|
@@ -202,7 +169,7 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
202
169
|
).then((action) => {
|
|
203
170
|
if (!resumeInstance.rejected.match(action)) return;
|
|
204
171
|
const current = getState().conversations.byConversationId[conversationId]?.serverOperation;
|
|
205
|
-
if (current?.executionId !==
|
|
172
|
+
if (current?.executionId !== waiting.execution_id) return;
|
|
206
173
|
const next = decideAfterContinueRefused({
|
|
207
174
|
originalErrorName: action.meta.originalErrorName,
|
|
208
175
|
liveStream: hasAbortController(conversationId)
|
|
@@ -216,25 +183,24 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
216
183
|
} catch (err) {
|
|
217
184
|
console.error(
|
|
218
185
|
"[runtime-reconnect] pending-call recovery check failed; preserving an explicit retry affordance.",
|
|
219
|
-
{ conversationId, executionId:
|
|
186
|
+
{ conversationId, executionId: waiting.execution_id, err }
|
|
220
187
|
);
|
|
221
188
|
stampOperation("waiting_input", true, "needs_action");
|
|
222
189
|
} finally {
|
|
223
190
|
waitingRecoveryInFlight = false;
|
|
224
191
|
}
|
|
225
192
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (!op.waiting_input && op.request_id) {
|
|
193
|
+
const consumeRejoin = async ({
|
|
194
|
+
operation,
|
|
195
|
+
requestId: rejoinId
|
|
196
|
+
}) => {
|
|
197
|
+
rejoinRan = true;
|
|
232
198
|
const activeRequests = getState().activeRequests.byRequestId;
|
|
233
199
|
const hintedRequest = requestId ? activeRequests[requestId] : void 0;
|
|
234
200
|
const matchingRequest = Object.values(activeRequests).find(
|
|
235
|
-
(candidate) => candidate.conversationId === conversationId && (candidate.requestId ===
|
|
201
|
+
(candidate) => candidate.conversationId === conversationId && (candidate.requestId === rejoinId || candidate.serverRequestId === rejoinId || candidate.requestId === operation.request_id || candidate.serverRequestId === operation.request_id)
|
|
236
202
|
);
|
|
237
|
-
const localRequestId = hintedRequest?.conversationId === conversationId ? hintedRequest.requestId : matchingRequest?.requestId ??
|
|
203
|
+
const localRequestId = hintedRequest?.conversationId === conversationId ? hintedRequest.requestId : matchingRequest?.requestId ?? rejoinId;
|
|
238
204
|
dispatch(createRequest({ requestId: localRequestId, conversationId }));
|
|
239
205
|
dispatch(
|
|
240
206
|
setRequestStatus({ requestId: localRequestId, status: "connecting" })
|
|
@@ -243,7 +209,7 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
243
209
|
await runAiStream({
|
|
244
210
|
requestId: localRequestId,
|
|
245
211
|
conversationId,
|
|
246
|
-
url: `${backend.baseUrl}${runtimeOperationRejoinPath(
|
|
212
|
+
url: `${backend.baseUrl}${runtimeOperationRejoinPath(rejoinId)}`,
|
|
247
213
|
headers: backend.headers,
|
|
248
214
|
body: {},
|
|
249
215
|
channel: backend.channel,
|
|
@@ -258,117 +224,159 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
258
224
|
);
|
|
259
225
|
}
|
|
260
226
|
});
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (
|
|
275
|
-
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (error instanceof StreamRejoinUnavailableError) throw error;
|
|
229
|
+
standDown = true;
|
|
230
|
+
return "stand-down";
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
let outcome;
|
|
234
|
+
try {
|
|
235
|
+
outcome = await followConversationServerJob(backend, conversationId, {
|
|
236
|
+
signal: ctrl.signal,
|
|
237
|
+
// org-filter: server-call the conversation's own organization rides the backend headers; the read is by conversation id
|
|
238
|
+
retry: (attempt) => retryReconnect(attempt, ctrl.signal, stillOwner),
|
|
239
|
+
onStatus: (status, operation) => {
|
|
240
|
+
if (status === "connecting") return;
|
|
241
|
+
if (!identified) {
|
|
242
|
+
identified = true;
|
|
243
|
+
if (!stillOwner()) {
|
|
244
|
+
superseded = true;
|
|
245
|
+
ctrl.abort();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
276
248
|
}
|
|
249
|
+
if (!operation) return;
|
|
250
|
+
op = operation;
|
|
251
|
+
if (!sawLive && !operation.is_terminal) {
|
|
252
|
+
sawLive = true;
|
|
253
|
+
console.warn(
|
|
254
|
+
"[runtime-reconnect] non-terminal server operation found \u2014 following its event stream.",
|
|
255
|
+
{
|
|
256
|
+
conversationId,
|
|
257
|
+
executionId: operation.execution_id,
|
|
258
|
+
status: operation.status,
|
|
259
|
+
source
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
stampOperation(
|
|
263
|
+
operation.status,
|
|
264
|
+
operation.waiting_input,
|
|
265
|
+
operation.waiting_input ? "checking_for_prompt" : void 0
|
|
266
|
+
);
|
|
267
|
+
if (operation.waiting_input) void recoverWaitingInput();
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
rejoinConsumer: consumeRejoin,
|
|
271
|
+
onRejoinUnavailable: () => {
|
|
272
|
+
const current = liveOp();
|
|
273
|
+
console.warn(
|
|
274
|
+
"[runtime-reconnect] live response replay unavailable \u2014 following durable lifecycle instead.",
|
|
275
|
+
{ conversationId, executionId: current.execution_id }
|
|
276
|
+
);
|
|
277
|
+
captureError({
|
|
278
|
+
source: "agent-stream-warning",
|
|
279
|
+
code: "live_replay_unavailable",
|
|
280
|
+
message: "A running response could not replay its live NDJSON; following durable lifecycle until the saved result is available.",
|
|
281
|
+
requestId: current.request_id ?? void 0,
|
|
282
|
+
conversationId,
|
|
283
|
+
raw: { executionId: current.execution_id, source }
|
|
284
|
+
});
|
|
285
|
+
stampOperation(current.status, false, "live_replay_unavailable");
|
|
286
|
+
},
|
|
287
|
+
afterRejoin: () => {
|
|
288
|
+
if (!stillOwner()) {
|
|
289
|
+
superseded = true;
|
|
290
|
+
return "handoff";
|
|
291
|
+
}
|
|
292
|
+
clearFollower();
|
|
277
293
|
void dispatch(
|
|
278
294
|
reconnectServerOperation({ conversationId, source: "cold-load" })
|
|
279
295
|
);
|
|
280
|
-
return
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
296
|
+
return "handoff";
|
|
297
|
+
},
|
|
298
|
+
onOperationEvent: (event) => {
|
|
299
|
+
if (hasAbortController(conversationId)) {
|
|
300
|
+
ctrl.abort();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (event.kind === "waiting_input") {
|
|
304
|
+
void recoverWaitingInput();
|
|
305
|
+
} else if (RUNNING_KINDS.has(event.kind)) {
|
|
306
|
+
stampOperation("running", false);
|
|
307
|
+
} else if (event.kind === "paused") {
|
|
308
|
+
stampOperation("paused", false);
|
|
292
309
|
}
|
|
293
|
-
|
|
310
|
+
},
|
|
311
|
+
reloadSavedResult: async ({ status }) => {
|
|
312
|
+
if (status === "none") return;
|
|
313
|
+
if (rejoinRan && !stillOwner()) {
|
|
314
|
+
superseded = true;
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (!sawLive && source === "stream-loss") {
|
|
318
|
+
fallbackToLegacyRecovery();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
await dispatch(loadConversation({ conversationId })).unwrap();
|
|
294
322
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
});
|
|
307
|
-
stampOperation(op.status, false, "live_replay_unavailable");
|
|
308
|
-
}
|
|
323
|
+
});
|
|
324
|
+
} catch (err) {
|
|
325
|
+
const stale = ctrl.signal.aborted || !stillOwner();
|
|
326
|
+
clearFollower();
|
|
327
|
+
if (stale) return noFollow;
|
|
328
|
+
console.warn(
|
|
329
|
+
"[runtime-reconnect] operations-by-link fetch failed \u2014 falling back to legacy recovery.",
|
|
330
|
+
{ conversationId, err }
|
|
331
|
+
);
|
|
332
|
+
if (source === "stream-loss") fallbackToLegacyRecovery();
|
|
333
|
+
return noFollow;
|
|
309
334
|
}
|
|
310
|
-
if (
|
|
311
|
-
|
|
335
|
+
if (superseded) {
|
|
336
|
+
clearFollower();
|
|
337
|
+
return noFollow;
|
|
312
338
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
if (followers.get(conversationId) === ctrl) {
|
|
333
|
-
followers.delete(conversationId);
|
|
339
|
+
if (standDown) {
|
|
340
|
+
clearFollower();
|
|
341
|
+
return noFollow;
|
|
342
|
+
}
|
|
343
|
+
if (outcome.path === "rejoin" && !outcome.settled && !outcome.aborted) {
|
|
344
|
+
clearFollower();
|
|
345
|
+
return { followed: true, finalStatus: null };
|
|
346
|
+
}
|
|
347
|
+
clearFollower();
|
|
348
|
+
if (outcome.status === "none") {
|
|
349
|
+
if (source === "stream-loss") fallbackToLegacyRecovery();
|
|
350
|
+
return noFollow;
|
|
351
|
+
}
|
|
352
|
+
if (outcome.path === "already_settled" && !sawLive) {
|
|
353
|
+
if (outcome.reloadError !== void 0) throw outcome.reloadError;
|
|
354
|
+
return noFollow;
|
|
334
355
|
}
|
|
335
|
-
if (
|
|
356
|
+
if (outcome.aborted) {
|
|
336
357
|
if (followers.has(conversationId)) return noFollow;
|
|
337
358
|
dispatch(patchConversation({ conversationId, serverOperation: null }));
|
|
338
359
|
return noFollow;
|
|
339
360
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
executionId:
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
},
|
|
351
|
-
onStillRunning: () => {
|
|
352
|
-
console.warn(
|
|
353
|
-
"[runtime-reconnect] event stream unavailable after retries \u2014 falling back to legacy recovery.",
|
|
354
|
-
{ conversationId, executionId: op.execution_id }
|
|
355
|
-
);
|
|
356
|
-
dispatch(patchConversation({ conversationId, serverOperation: null }));
|
|
357
|
-
fallbackToLegacyRecovery();
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
);
|
|
361
|
-
if (settlement.state !== "settled") return noFollow;
|
|
362
|
-
if (!settlement.reloaded) {
|
|
361
|
+
if (!outcome.settled) {
|
|
362
|
+
console.warn(
|
|
363
|
+
"[runtime-reconnect] event stream unavailable after retries \u2014 falling back to legacy recovery.",
|
|
364
|
+
{ conversationId, executionId: outcome.executionId }
|
|
365
|
+
);
|
|
366
|
+
dispatch(patchConversation({ conversationId, serverOperation: null }));
|
|
367
|
+
fallbackToLegacyRecovery();
|
|
368
|
+
return noFollow;
|
|
369
|
+
}
|
|
370
|
+
if (!outcome.reloaded) {
|
|
363
371
|
console.warn(
|
|
364
372
|
"[runtime-reconnect] operation settled but the conversation refetch failed.",
|
|
365
|
-
{ conversationId, err:
|
|
373
|
+
{ conversationId, err: outcome.reloadError }
|
|
366
374
|
);
|
|
367
375
|
dispatch(patchConversation({ conversationId, serverOperation: null }));
|
|
368
|
-
return { followed: true, finalStatus:
|
|
376
|
+
return { followed: true, finalStatus: outcome.status };
|
|
369
377
|
}
|
|
370
378
|
dispatch(patchConversation({ conversationId, serverOperation: null }));
|
|
371
|
-
const finalStatus =
|
|
379
|
+
const finalStatus = outcome.status;
|
|
372
380
|
if (finalStatus === "completed") {
|
|
373
381
|
const rid = requestId ?? selectLatestRequestId(conversationId)(getState());
|
|
374
382
|
if (rid)
|
|
@@ -386,7 +394,7 @@ const reconnectServerOperation = createAsyncThunk(
|
|
|
386
394
|
}
|
|
387
395
|
console.warn("[runtime-reconnect] server operation settled \u2014 rehydrated.", {
|
|
388
396
|
conversationId,
|
|
389
|
-
executionId:
|
|
397
|
+
executionId: outcome.executionId,
|
|
390
398
|
finalStatus
|
|
391
399
|
});
|
|
392
400
|
return { followed: true, finalStatus };
|
|
@@ -20,6 +20,8 @@ export interface RuntimeOperationView {
|
|
|
20
20
|
execution_id: string;
|
|
21
21
|
/** Durable runtime/chat request identity used by the resume endpoint. */
|
|
22
22
|
request_id: string | null;
|
|
23
|
+
/** Newer by-link shape: the root run to rejoin by (preferred over `request_id`). */
|
|
24
|
+
root_request_id?: string | null;
|
|
23
25
|
type: string;
|
|
24
26
|
status: RuntimeExecutionStatus;
|
|
25
27
|
is_terminal: boolean;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP connections client — aidream `/api/mcp-connections/*`.
|
|
3
|
+
*
|
|
4
|
+
* Phase 4 of the Unified Credential Vault cutover: MCP tokens/credentials
|
|
5
|
+
* live ONLY in vault items on the server. The browser never sees a token —
|
|
6
|
+
* discovery, invocation, refresh, credential persistence, and disconnect all
|
|
7
|
+
* run in aidream with vault-resolved auth. The connection row
|
|
8
|
+
* (tool.mcp_user_conn) is non-secret metadata the FE may read directly.
|
|
9
|
+
*
|
|
10
|
+
* (Replaces the deleted Next.js /api/mcp/servers/[serverId]/* routes and the
|
|
11
|
+
* deleted mcp-client/token-refresh.ts.)
|
|
12
|
+
*/
|
|
1
13
|
import type { McpToolSchema } from "./mcp-client/tool-discovery.js";
|
|
2
14
|
import type { components } from "@ai-matrx/agents/generated/api-types";
|
|
3
15
|
import type { AttachableAvailability } from "../connectors/attachable-resources.js";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createClient } from "../../host/db.js";
|
|
2
1
|
import { productionUrl } from "../../host/server/endpoints.js";
|
|
3
2
|
import {
|
|
4
3
|
buildMatrxRequestUrl,
|
|
@@ -7,22 +6,20 @@ import {
|
|
|
7
6
|
} from "@ai-matrx/agents/matrx";
|
|
8
7
|
import { applyOrganizationContextHeader } from "../../host/server/organization-context.js";
|
|
9
8
|
import { ensureOrganizationForRequest } from "../../host/org.js";
|
|
9
|
+
import { readSessionToken } from "../../host/identity.js";
|
|
10
10
|
function backendBase() {
|
|
11
11
|
return productionUrl();
|
|
12
12
|
}
|
|
13
13
|
async function authHeaders(method, explicitOrganizationId) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
data: { session }
|
|
17
|
-
} = await supabase.auth.getSession();
|
|
18
|
-
if (!session?.access_token) throw new Error("Not signed in");
|
|
14
|
+
const accessToken = await readSessionToken();
|
|
15
|
+
if (!accessToken) throw new Error("Not signed in");
|
|
19
16
|
const organizationId = await ensureOrganizationForRequest({
|
|
20
17
|
method,
|
|
21
18
|
organizationId: explicitOrganizationId
|
|
22
19
|
});
|
|
23
20
|
return applyOrganizationContextHeader(
|
|
24
21
|
{
|
|
25
|
-
Authorization: `Bearer ${
|
|
22
|
+
Authorization: `Bearer ${accessToken}`,
|
|
26
23
|
"Content-Type": "application/json"
|
|
27
24
|
},
|
|
28
25
|
organizationId
|
|
@@ -137,6 +137,12 @@ export interface ActiveRequest {
|
|
|
137
137
|
* Absent only for older servers that did not stamp a segment id.
|
|
138
138
|
*/
|
|
139
139
|
transportStreamId?: string | null;
|
|
140
|
+
/**
|
|
141
|
+
* High-water `stream_seq` per emitter segment seen so far. A rejoin replays
|
|
142
|
+
* every segment from frame one; this is what drops segment A again after the
|
|
143
|
+
* page live-followed A then B.
|
|
144
|
+
*/
|
|
145
|
+
transportSeqByStream?: Record<string, number>;
|
|
140
146
|
/**
|
|
141
147
|
* User-applied override for the rendered text. When non-null, this
|
|
142
148
|
* supersedes the server-derived text built from `renderBlocks` for the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isMaterializedArtifactId } from "
|
|
1
|
+
import { isMaterializedArtifactId } from "@ai-matrx/rich-content/utils/lifted/artifactId";
|
|
2
2
|
const CANVAS_ITEM_SOURCE_KIND = "canvas_item";
|
|
3
3
|
const DESCRIPTION = "A pinned conversation artifact (code or JSON). Read with ctx_get on this key (the artifact UUID). Apply every change with ctx_patch \u2014 edits save as a new version of the same artifact. Do NOT recreate it; extend or edit it.";
|
|
4
4
|
function isCanvasItemContextKey(key) {
|