theokit 0.36.1 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{actions-virtual-module-SYGQE6JQ.js → actions-virtual-module-H3LY5Q22.js} +3 -3
- package/dist/{actions-virtual-module-3KSP3WPJ.js → actions-virtual-module-WLOT2SPR.js} +3 -3
- package/dist/{agent-DX5LHD6X.js → agent-VQMWM5BK.js} +2 -2
- package/dist/{app-typed-client-N4TRX5FI.js → app-typed-client-67GBLYD6.js} +113 -24
- package/dist/app-typed-client-67GBLYD6.js.map +1 -0
- package/dist/{app-typed-client-ESMMLPYK.js → app-typed-client-SPFNGVZL.js} +113 -24
- package/dist/app-typed-client-SPFNGVZL.js.map +1 -0
- package/dist/{build-3C2KQPME.js → build-3BFIACHN.js} +6 -6
- package/dist/{chunk-RVJAM77M.js → chunk-2HKRQUWU.js} +2 -2
- package/dist/{chunk-3KD2O6DC.js → chunk-BKYFOEBK.js} +58 -54
- package/dist/chunk-BKYFOEBK.js.map +1 -0
- package/dist/{chunk-V3X5FWJA.js → chunk-EV33DOG6.js} +1 -1
- package/dist/{chunk-V3X5FWJA.js.map → chunk-EV33DOG6.js.map} +1 -1
- package/dist/{chunk-UDG6JPVP.js → chunk-GW5H4KB4.js} +2 -2
- package/dist/{chunk-DISADTFZ.js → chunk-IPSFSNEJ.js} +2 -2
- package/dist/{chunk-RPMKF6BC.js → chunk-OLZLYLOE.js} +109 -7
- package/dist/chunk-OLZLYLOE.js.map +1 -0
- package/dist/{chunk-CEHXC5NI.js → chunk-Q3C3KH3C.js} +24 -5
- package/dist/chunk-Q3C3KH3C.js.map +1 -0
- package/dist/{chunk-MNQC5XEJ.js → chunk-QCB7ZNR2.js} +94 -37
- package/dist/chunk-QCB7ZNR2.js.map +1 -0
- package/dist/{chunk-T4TCLEFB.js → chunk-QMV3R6D5.js} +91 -36
- package/dist/chunk-QMV3R6D5.js.map +1 -0
- package/dist/chunk-QNFVYST4.js +110 -0
- package/dist/chunk-QNFVYST4.js.map +1 -0
- package/dist/{chunk-WL4AX5FI.js → chunk-QWBXUZAX.js} +57 -54
- package/dist/chunk-QWBXUZAX.js.map +1 -0
- package/dist/cli/index.js +5 -5
- package/dist/client/core.d.ts +12 -1
- package/dist/client/core.js +1 -1
- package/dist/client/index.d.ts +7 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/controller-swc-transform-URELVCBM.js +27 -0
- package/dist/controller-swc-transform-URELVCBM.js.map +1 -0
- package/dist/controller-swc-transform-XV3ETSE3.js +28 -0
- package/dist/controller-swc-transform-XV3ETSE3.js.map +1 -0
- package/dist/{dev-CNPT7COE.js → dev-IBCZYFZZ.js} +8 -8
- package/dist/{index-C3ged4mn.d.ts → index-BeAsvfPr.d.ts} +2 -2
- package/dist/index.js +4 -4
- package/dist/{internal-api-4IXBITVV.js → internal-api-MDCX7A7H.js} +10 -3
- package/dist/{internal-api-5JKDYRGM.js → internal-api-OLNM25HS.js} +9 -3
- package/dist/{mcp-INKDONQS.js → mcp-DKRODZAL.js} +2 -2
- package/dist/{plugin-runner-CMprWWHZ.d.ts → plugin-runner-BFEcgtmJ.d.ts} +1 -1
- package/dist/{plugin-types-L49QYMb5.d.ts → plugin-types-D69-lJZ9.d.ts} +9 -2
- package/dist/{registry-3NB7KOUI.js → registry-FKPH7XEL.js} +1 -1
- package/dist/server/define/index.d.ts +1 -1
- package/dist/server/define/index.js +1 -1
- package/dist/server/http/index.d.ts +3 -3
- package/dist/server/http/index.js +1 -1
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/plugins/index.d.ts +3 -3
- package/dist/{start-LNJZNKF6.js → start-WB7GYXEW.js} +3 -3
- package/dist/vite-plugin/index.js +4 -4
- package/dist/{vite-plugin-NC5ETNMV.js → vite-plugin-5Y3LDTXI.js} +5 -5
- package/package.json +2 -2
- package/dist/app-typed-client-ESMMLPYK.js.map +0 -1
- package/dist/app-typed-client-N4TRX5FI.js.map +0 -1
- package/dist/chunk-3KD2O6DC.js.map +0 -1
- package/dist/chunk-CEHXC5NI.js.map +0 -1
- package/dist/chunk-MNQC5XEJ.js.map +0 -1
- package/dist/chunk-RPMKF6BC.js.map +0 -1
- package/dist/chunk-RSVN727G.js +0 -1
- package/dist/chunk-T4TCLEFB.js.map +0 -1
- package/dist/chunk-WL4AX5FI.js.map +0 -1
- package/dist/vite-plugin-NC5ETNMV.js.map +0 -1
- /package/dist/{actions-virtual-module-SYGQE6JQ.js.map → actions-virtual-module-H3LY5Q22.js.map} +0 -0
- /package/dist/{actions-virtual-module-3KSP3WPJ.js.map → actions-virtual-module-WLOT2SPR.js.map} +0 -0
- /package/dist/{agent-DX5LHD6X.js.map → agent-VQMWM5BK.js.map} +0 -0
- /package/dist/{build-3C2KQPME.js.map → build-3BFIACHN.js.map} +0 -0
- /package/dist/{chunk-RVJAM77M.js.map → chunk-2HKRQUWU.js.map} +0 -0
- /package/dist/{chunk-UDG6JPVP.js.map → chunk-GW5H4KB4.js.map} +0 -0
- /package/dist/{chunk-DISADTFZ.js.map → chunk-IPSFSNEJ.js.map} +0 -0
- /package/dist/{dev-CNPT7COE.js.map → dev-IBCZYFZZ.js.map} +0 -0
- /package/dist/{chunk-RSVN727G.js.map → internal-api-MDCX7A7H.js.map} +0 -0
- /package/dist/{internal-api-4IXBITVV.js.map → internal-api-OLNM25HS.js.map} +0 -0
- /package/dist/{mcp-INKDONQS.js.map → mcp-DKRODZAL.js.map} +0 -0
- /package/dist/{registry-3NB7KOUI.js.map → registry-FKPH7XEL.js.map} +0 -0
- /package/dist/{start-LNJZNKF6.js.map → start-WB7GYXEW.js.map} +0 -0
- /package/dist/{internal-api-5JKDYRGM.js.map → vite-plugin-5Y3LDTXI.js.map} +0 -0
|
@@ -54,7 +54,14 @@ var AgentClient = class {
|
|
|
54
54
|
#status = "idle";
|
|
55
55
|
#error;
|
|
56
56
|
#controller = null;
|
|
57
|
-
|
|
57
|
+
// M46 — conversation accumulation (React-free; all surfaces inherit it via the snapshot).
|
|
58
|
+
/** Committed (finished) turns — user + assistant, with stable fabricated ids. */
|
|
59
|
+
#committed = [];
|
|
60
|
+
/** The current turn's user message (in `thread` but never in `messages` — back-compat). */
|
|
61
|
+
#currentUser;
|
|
62
|
+
/** A stable id for the current turn's assistant (the SDK leaves it empty — we fabricate one). */
|
|
63
|
+
#currentAssistantId = "";
|
|
64
|
+
#snapshot = { messages: [], thread: [], status: "idle", error: void 0 };
|
|
58
65
|
constructor(transport, contextResolver) {
|
|
59
66
|
this.#transport = transport;
|
|
60
67
|
this.#contextResolver = contextResolver;
|
|
@@ -69,7 +76,8 @@ var AgentClient = class {
|
|
|
69
76
|
/** The current immutable snapshot (stable reference until the next emit). */
|
|
70
77
|
getSnapshot = () => this.#snapshot;
|
|
71
78
|
#emit() {
|
|
72
|
-
this.#
|
|
79
|
+
const thread = this.#currentUser ? [...this.#committed, this.#currentUser, ...this.#messages] : [...this.#committed, ...this.#messages];
|
|
80
|
+
this.#snapshot = { messages: this.#messages, thread, status: this.#status, error: this.#error };
|
|
73
81
|
for (const listener of this.#listeners) listener();
|
|
74
82
|
}
|
|
75
83
|
#upsert(message) {
|
|
@@ -91,7 +99,8 @@ var AgentClient = class {
|
|
|
91
99
|
}
|
|
92
100
|
await consumeChunkStream(stream, (message) => {
|
|
93
101
|
if (aborted()) return;
|
|
94
|
-
this.#
|
|
102
|
+
const stamped = message.id ? message : { ...message, id: this.#currentAssistantId };
|
|
103
|
+
this.#upsert(stamped);
|
|
95
104
|
this.#emit();
|
|
96
105
|
});
|
|
97
106
|
if (aborted()) return;
|
|
@@ -106,9 +115,15 @@ var AgentClient = class {
|
|
|
106
115
|
}
|
|
107
116
|
/** Send a typed input; opens a fresh stream (replaces prior messages). */
|
|
108
117
|
send = (input) => {
|
|
118
|
+
if (this.#status === "done" && this.#currentUser) {
|
|
119
|
+
this.#committed = [...this.#committed, this.#currentUser, ...this.#messages];
|
|
120
|
+
}
|
|
109
121
|
this.abort();
|
|
110
122
|
const controller = new AbortController();
|
|
111
123
|
this.#controller = controller;
|
|
124
|
+
const userMsg = buildUserMessage(input);
|
|
125
|
+
this.#currentUser = userMsg;
|
|
126
|
+
this.#currentAssistantId = crypto.randomUUID();
|
|
112
127
|
this.#messages = [];
|
|
113
128
|
this.#error = void 0;
|
|
114
129
|
this.#status = "streaming";
|
|
@@ -119,7 +134,7 @@ var AgentClient = class {
|
|
|
119
134
|
trigger: "submit-message",
|
|
120
135
|
chatId: this.#chatId,
|
|
121
136
|
messageId: void 0,
|
|
122
|
-
messages: [
|
|
137
|
+
messages: [userMsg],
|
|
123
138
|
abortSignal: controller.signal,
|
|
124
139
|
// Only object inputs flow as the request `body` (the turn text is always in `messages`);
|
|
125
140
|
// a primitive input is carried by the user message, never spread into the body.
|
|
@@ -135,6 +150,8 @@ var AgentClient = class {
|
|
|
135
150
|
reconnect = () => {
|
|
136
151
|
const controller = new AbortController();
|
|
137
152
|
this.#controller = controller;
|
|
153
|
+
if (!this.#currentAssistantId) this.#currentAssistantId = crypto.randomUUID();
|
|
154
|
+
this.#error = void 0;
|
|
138
155
|
this.#status = "streaming";
|
|
139
156
|
this.#emit();
|
|
140
157
|
const context = this.#contextResolver?.();
|
|
@@ -156,6 +173,8 @@ var AgentClient = class {
|
|
|
156
173
|
reset = () => {
|
|
157
174
|
this.abort();
|
|
158
175
|
this.#messages = [];
|
|
176
|
+
this.#committed = [];
|
|
177
|
+
this.#currentUser = void 0;
|
|
159
178
|
this.#error = void 0;
|
|
160
179
|
this.#status = "idle";
|
|
161
180
|
this.#emit();
|
|
@@ -467,4 +486,4 @@ export {
|
|
|
467
486
|
ChannelTransport,
|
|
468
487
|
createAgentClient
|
|
469
488
|
};
|
|
470
|
-
//# sourceMappingURL=chunk-
|
|
489
|
+
//# sourceMappingURL=chunk-Q3C3KH3C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/consume-ui-message-stream.ts","../src/client/agent-client.ts","../src/client/http-transport.ts","../src/client/last-user-text.ts","../src/client/in-process-transport.ts","../src/client/channel-transport.ts","../src/client/create-agent-client.ts"],"sourcesContent":["import type { UIMessage, UIMessageChunk } from 'ai'\n\n/**\n * M2 (theokit-ai-first) — read a TheoKit agent endpoint's `UIMessageStream` SSE `Response`\n * into reconstructed assistant `UIMessage`s, reusing the `ai` package's own consumer\n * primitives (`parseJsonEventStream` + `readUIMessageStream`) — the exact path\n * `@ai-sdk/react`'s `useChat` runs internally. No reinvented wire parser (Rule 9).\n *\n * `ai` is an OPTIONAL peer dependency, so it is imported dynamically: an app that never\n * calls an agent never pays for it, and importing `theokit/client` does not hard-require\n * `ai` (mirrors how the agent runtime dynamically imports `@theokit/sdk`). An agent app\n * always has `ai` installed (it is the UIMessageStream consumer).\n *\n * `onMessage` is invoked on every reconstruction step with the latest snapshot of the\n * assistant message, so a caller (the `useAgent` hook) can render streaming updates.\n */\nexport async function consumeUIMessageStream(\n response: Response,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n const chunkStream = await responseToChunkStream(response)\n await consumeChunkStream(chunkStream, onMessage)\n}\n\n/**\n * M41 (ADR-0050 D3) — the reusable middle piece: a UIMessageStream SSE `Response` →\n * `ReadableStream<UIMessageChunk>`, reusing `ai`'s own `parseJsonEventStream` (the exact primitive\n * `useChat` runs). This is precisely what a `ChatTransport.sendMessages` returns, so `HttpTransport`\n * builds on it directly (no reinvented wire parser — Rule 9). A body-less response yields an empty stream.\n */\nexport async function responseToChunkStream(\n response: Response,\n): Promise<ReadableStream<UIMessageChunk>> {\n if (response.body === null) {\n return new ReadableStream<UIMessageChunk>({\n start(controller) {\n controller.close()\n },\n })\n }\n\n const { parseJsonEventStream, uiMessageChunkSchema } = await import('ai')\n\n // ai validates each SSE JSON frame against its own strict chunk schema (the exact gate `useChat`\n // runs), then yields `{ success, value }`; forward the valid chunks.\n const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema })\n return new ReadableStream<UIMessageChunk>({\n async start(controller) {\n for await (const result of parsed) {\n if (result.success) controller.enqueue(result.value)\n }\n controller.close()\n },\n })\n}\n\n/**\n * M41 (ADR-0050 D6) — read a `ReadableStream<UIMessageChunk>` into reconstructed assistant\n * `UIMessage`s via `ai`'s `readUIMessageStream`. Shared by `consumeUIMessageStream` (Response path)\n * and the framework-agnostic `AgentClient` store (transport path). `onMessage` fires on every\n * reconstruction step so a caller can render streaming updates.\n */\nexport async function consumeChunkStream(\n stream: ReadableStream<UIMessageChunk>,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n const { readUIMessageStream } = await import('ai')\n for await (const message of readUIMessageStream({ stream })) {\n onMessage(message)\n }\n}\n","import type { UIMessage, UIMessageChunk } from 'ai'\n\nimport { consumeChunkStream } from './consume-ui-message-stream.js'\nimport type { AgentTransport, ApprovalDecision, RequestContext } from './transport.js'\n\nexport type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error'\n\n/** The observable state the store exposes (stable reference between emits — `useSyncExternalStore` contract). */\nexport interface AgentClientState {\n /** The CURRENT turn's assistant messages (per-turn; reset each `send`). Back-compat — unchanged since M41. */\n messages: UIMessage[]\n /**\n * M46 — the full conversation: committed turns + the current turn's user message + in-flight assistant.\n * Accumulated across sends (never reset except by `reset()`), with stable ids committed exactly once.\n * Render this instead of hand-rolling a transcript from `messages`.\n */\n thread: UIMessage[]\n status: UseAgentStatus\n error: Error | undefined\n}\n\n/** Derive the turn text from a typed input: `input.message` when present, else the serialized input. */\nfunction inputToText(input: unknown): string {\n if (\n typeof input === 'object' &&\n input !== null &&\n typeof (input as { message?: unknown }).message === 'string'\n ) {\n return (input as { message: string }).message\n }\n if (typeof input === 'string') return input\n return JSON.stringify(input)\n}\n\n/** Build a user `UIMessage` from a typed input (text from `{ message }`, else the serialized input). */\nfunction buildUserMessage(input: unknown): UIMessage {\n return {\n id: crypto.randomUUID(),\n role: 'user',\n parts: [{ type: 'text', text: inputToText(input) }],\n }\n}\n\n/**\n * M41 (ADR-0050 D6) — the framework-agnostic agent client store.\n *\n * Holds `messages`/`status`/`error`, drives an {@link AgentTransport}, and notifies subscribers on\n * change. It is the SINGLE consolidation point: web (`HttpTransport`) and terminal/desktop\n * (`InProcessTransport`) run the SAME store. `useAgent` is a thin React binding over it via\n * `useSyncExternalStore`; a standalone (no-React) client (M44) can subscribe directly. Being\n * framework-agnostic, it is unit-tested without a DOM.\n */\nexport class AgentClient<TInput = unknown> {\n readonly #transport: AgentTransport\n readonly #chatId = crypto.randomUUID()\n readonly #listeners = new Set<() => void>()\n /** M43 — resolves per-request context (evaluated on every send/reconnect — dynamic, never stale). */\n readonly #contextResolver: (() => RequestContext | undefined) | undefined\n\n #messages: UIMessage[] = []\n #status: UseAgentStatus = 'idle'\n #error: Error | undefined\n #controller: AbortController | null = null\n // M46 — conversation accumulation (React-free; all surfaces inherit it via the snapshot).\n /** Committed (finished) turns — user + assistant, with stable fabricated ids. */\n #committed: UIMessage[] = []\n /** The current turn's user message (in `thread` but never in `messages` — back-compat). */\n #currentUser: UIMessage | undefined\n /** A stable id for the current turn's assistant (the SDK leaves it empty — we fabricate one). */\n #currentAssistantId = ''\n #snapshot: AgentClientState = { messages: [], thread: [], status: 'idle', error: undefined }\n\n constructor(transport: AgentTransport, contextResolver?: () => RequestContext | undefined) {\n this.#transport = transport\n this.#contextResolver = contextResolver\n }\n\n /** Subscribe to state changes; returns an unsubscribe fn. */\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener)\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n /** The current immutable snapshot (stable reference until the next emit). */\n getSnapshot = (): AgentClientState => this.#snapshot\n\n #emit(): void {\n // thread = committed turns + this turn's user + the in-flight assistant (`messages`). New array per\n // emit is fine — the SNAPSHOT reference only changes here, satisfying useSyncExternalStore.\n const thread = this.#currentUser\n ? [...this.#committed, this.#currentUser, ...this.#messages]\n : [...this.#committed, ...this.#messages]\n this.#snapshot = { messages: this.#messages, thread, status: this.#status, error: this.#error }\n for (const listener of this.#listeners) listener()\n }\n\n #upsert(message: UIMessage): void {\n const next = [...this.#messages]\n const idx = next.findIndex((existing) => existing.id === message.id)\n if (idx >= 0) next[idx] = message\n else next.push(message)\n this.#messages = next\n }\n\n async #drive(\n open: () => Promise<ReadableStream<UIMessageChunk> | null>,\n controller: AbortController,\n ): Promise<void> {\n // Read via a function (not a narrowed local) — `aborted` flips ASYNC across the awaits below, so the\n // control-flow narrowing of a direct `signal.aborted` read would be wrong. A stale drive (its\n // controller aborted because a newer send/abort took over) MUST NOT clobber the newer status.\n const aborted = (): boolean => controller.signal.aborted\n try {\n const stream = await open()\n if (aborted()) return\n if (stream === null) {\n // Nothing to resume (e.g. reconnect after the run completed). Settle without error.\n this.#status = this.#messages.length > 0 ? 'done' : 'idle'\n this.#emit()\n return\n }\n await consumeChunkStream(stream, (message) => {\n if (aborted()) return\n // The SDK leaves the assistant message id empty — fabricate a stable per-turn id so every chunk\n // upserts into the SAME message and the committed copy has a collision-free key (M46).\n const stamped = message.id ? message : { ...message, id: this.#currentAssistantId }\n this.#upsert(stamped)\n this.#emit()\n })\n if (aborted()) return\n this.#status = 'done'\n this.#emit()\n } catch (err) {\n if (aborted()) return\n this.#error = err instanceof Error ? err : new Error(String(err))\n this.#status = 'error'\n this.#emit()\n }\n }\n\n /** Send a typed input; opens a fresh stream (replaces prior messages). */\n send = (input: TInput): void => {\n // M46 — commit the PRIOR turn into history exactly once, but ONLY if it finished cleanly (`done`).\n // An errored or aborted turn (status !== 'done') is dropped, keeping committed history uncorrupted.\n if (this.#status === 'done' && this.#currentUser) {\n this.#committed = [...this.#committed, this.#currentUser, ...this.#messages]\n }\n this.abort()\n const controller = new AbortController()\n this.#controller = controller\n const userMsg = buildUserMessage(input)\n this.#currentUser = userMsg\n this.#currentAssistantId = crypto.randomUUID()\n this.#messages = []\n this.#error = undefined\n this.#status = 'streaming'\n this.#emit()\n const context = this.#contextResolver?.()\n void this.#drive(\n () =>\n this.#transport.sendMessages({\n trigger: 'submit-message',\n chatId: this.#chatId,\n messageId: undefined,\n messages: [userMsg],\n abortSignal: controller.signal,\n // Only object inputs flow as the request `body` (the turn text is always in `messages`);\n // a primitive input is carried by the user message, never spread into the body.\n body: typeof input === 'object' && input !== null ? input : undefined,\n // M43 — per-request context reaches every transport (headers → HTTP, metadata → in-process/channel).\n headers: context?.headers,\n metadata: context?.metadata,\n }),\n controller,\n )\n }\n\n /** Resume an interrupted stream via the transport's `reconnectToStream` (no-op when unavailable). */\n reconnect = (): void => {\n const controller = new AbortController()\n this.#controller = controller\n // Reconnecting before any send() (or after reset()) leaves #currentAssistantId empty — fabricate one\n // so a replayed assistant never lands in `thread` with an empty, non-unique id (M46 invariant).\n if (!this.#currentAssistantId) this.#currentAssistantId = crypto.randomUUID()\n // Reconnect means \"resume/retry\" — a stale error must not linger next to a fresh 'streaming' status.\n this.#error = undefined\n this.#status = 'streaming'\n this.#emit()\n const context = this.#contextResolver?.()\n void this.#drive(\n () =>\n this.#transport.reconnectToStream({\n chatId: this.#chatId,\n headers: context?.headers,\n metadata: context?.metadata,\n }),\n controller,\n )\n }\n\n /** Abort an in-flight stream (not an error — leaves messages as-is). */\n abort = (): void => {\n this.#controller?.abort()\n this.#controller = null\n }\n\n /** Clear messages + error, back to idle. */\n reset = (): void => {\n this.abort()\n this.#messages = []\n // M46 — reset means a NEW conversation: clear committed history + the current turn's user too.\n this.#committed = []\n this.#currentUser = undefined\n this.#error = undefined\n this.#status = 'idle'\n this.#emit()\n }\n\n /** Settle a paused HITL approval via the transport's HITL path (HTTP POST or inline callback). */\n approve = async (approvalId: string, decision: ApprovalDecision): Promise<void> => {\n await this.#transport.approve?.(approvalId, decision)\n }\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { responseToChunkStream } from './consume-ui-message-stream.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** Extra request headers — a static record OR a resolver called per request (for dynamic auth). */\nexport type HeadersResolver = Record<string, string> | (() => Record<string, string> | undefined)\n\nexport interface HttpTransportOptions {\n /** Agent endpoint path or URL, e.g. `/api/agents/support`. */\n api: string\n /**\n * Extra request headers (e.g. auth). Static record OR a resolver evaluated on EVERY request — pass a\n * resolver when the value is dynamic (a rotating JWT), so a stale header is never sent. Merged UNDER\n * per-request headers.\n */\n headers?: HeadersResolver\n /** Override fetch (primarily for tests / non-browser hosts) — static; resolved once at construction. */\n fetch?: typeof fetch\n}\n\n/** Normalize `ai`'s `Record | Headers | undefined` header option into a plain record. */\nfunction toRecord(headers: Record<string, string> | Headers | undefined): Record<string, string> {\n if (headers === undefined) return {}\n if (headers instanceof Headers) return Object.fromEntries(headers.entries())\n return headers\n}\n\n/**\n * M41 (ADR-0050 D3) — `ChatTransport` over the web agent path.\n *\n * - `sendMessages`: `POST <api>` with the UIMessageStream `accept` + the `X-Theo-Action` CSRF header\n * (HTTP method + headers are identical to the pre-M41 `useAgent` fetch; the body shape is a superset —\n * `{ ...input, messages: [UIMessage] }` — which the server's dual-path parser accepts, so no\n * regression), captures the server-minted `x-theokit-run-id`, and returns `ReadableStream<UIMessageChunk>`\n * via `ai`'s own SSE parser (`responseToChunkStream`).\n * - `reconnectToStream`: `GET <api>/runs/<runId>/stream` (M37 durable transport); 404 → `null` (the run\n * completed / was evicted). A caller may pass a `Last-Event-ID` header to resume only the tail; by\n * default the server replays the run from the start and the client upserts by message id (idempotent).\n * - `approve`: `POST <api>/approve/<id>` (out-of-band HITL settle).\n *\n * Implemented directly (not by subclassing `DefaultChatTransport`) because reconnect keys on our\n * server-minted `runId` captured from a response header, which the base class does not expose — see\n * ADR-0050 D3.\n */\nexport class HttpTransport implements AgentTransport {\n readonly #api: string\n readonly #headers: HeadersResolver\n readonly #fetch: typeof fetch\n /** Server-minted id of the last run (from `x-theokit-run-id`) — the reconnect key. */\n #lastRunId: string | undefined\n\n constructor(options: HttpTransportOptions) {\n this.#api = options.api\n this.#headers = options.headers ?? {}\n // BIND the default fetch to globalThis — the native `fetch` throws `TypeError: Illegal invocation` when\n // invoked as a method (`this.#fetch(...)` would set `this` to this transport instance, not the window).\n // An injected fetch (tests / non-browser hosts) is a plain function and is used as-is.\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis)\n }\n\n /** Resolve the configured headers per request (a resolver evaluates now — dynamic auth is never stale). */\n #resolveHeaders(): Record<string, string> {\n return (typeof this.#headers === 'function' ? this.#headers() : this.#headers) ?? {}\n }\n\n async sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, headers, body } = options\n // Only spread an object body (never a primitive — that would emit char-indexed keys). `body` is typed\n // `object | undefined` (ai's `ChatRequestOptions`), so no cast is needed — the guard narrows to\n // `object`. (A runtime-`null` body, which the type forbids, spreads to `{}` — harmless.) The server\n // accepts `{ messages }` (ai shape) AND `{ ...input }` (typed input), preferring the turn text.\n const extra = typeof body === 'object' ? body : undefined\n const response = await this.#fetch(this.#api, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...this.#resolveHeaders(),\n ...toRecord(headers),\n },\n body: JSON.stringify({ ...extra, messages }),\n signal: abortSignal,\n })\n if (!response.ok) {\n throw new Error(\n `Agent request to ${this.#api} failed: ${response.status} ${response.statusText}`,\n )\n }\n this.#lastRunId = response.headers.get('x-theokit-run-id') ?? undefined\n return responseToChunkStream(response)\n }\n\n async reconnectToStream(\n options: Parameters<ChatTransport<UIMessage>['reconnectToStream']>[0],\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n if (this.#lastRunId === undefined) return null\n const response = await this.#fetch(`${this.#api}/runs/${this.#lastRunId}/stream`, {\n method: 'GET',\n headers: { ...this.#resolveHeaders(), ...toRecord(options.headers) },\n })\n if (response.status === 404) return null\n if (!response.ok) {\n throw new Error(\n `Agent reconnect to run ${this.#lastRunId} failed: ${response.status} ${response.statusText}`,\n )\n }\n return responseToChunkStream(response)\n }\n\n async approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n const response = await this.#fetch(`${this.#api}/approve/${approvalId}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'X-Theo-Action': '1',\n ...this.#resolveHeaders(),\n },\n body: JSON.stringify(decision),\n })\n if (!response.ok) {\n throw new Error(`Approve ${approvalId} failed: ${response.status} ${response.statusText}`)\n }\n }\n}\n","import type { UIMessage } from 'ai'\n\n/**\n * M41/M42 — extract the turn text from the last user message's text parts. Shared by the transports\n * that hand a plain `message` string to an in-process/push runner (`InProcessTransport`,\n * `ChannelTransport`) rather than POSTing the `messages[]` array (`HttpTransport`). One definition (G12).\n */\nexport function extractLastUserText(messages: readonly UIMessage[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]\n if (message.role !== 'user') continue\n const text = message.parts\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map((part) => part.text)\n .join('')\n if (text.length > 0) return text\n }\n return ''\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { extractLastUserText } from './last-user-text.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** An inline approval request handed to the transport's resolver (structural — no server import). */\nexport interface InProcessApprovalRequestLike {\n approvalId: string\n toolName: string\n opts: unknown\n}\n\n/** Resolve one gated-tool approval inline (mirrors the SDK's `boolean | HitlDecision` return). */\nexport type InProcessAwaitApproval = (\n req: InProcessApprovalRequestLike,\n) => Promise<boolean | ApprovalDecision>\n\n/** The input the injected runner receives (structurally compatible with `StreamAgentTurnInProcessInput`). */\nexport interface InProcessRunInput {\n message: string\n sessionId?: string\n signal?: AbortSignal\n awaitApproval?: InProcessAwaitApproval\n /** M43 — per-request context (from `sendMessages`'s `metadata`) — tenant / provider / auth for the runner. */\n context?: unknown\n}\n\n/**\n * The in-process turn runner. The consumer binds `streamAgentTurnInProcess(mod, apiKey, …)`:\n * `new InProcessTransport({ run: (input) => streamAgentTurnInProcess(mod, apiKey, input) })`.\n * Injecting it keeps this client module decoupled from `server/` and makes the transport testable.\n */\nexport type InProcessRunner = (input: InProcessRunInput) => AsyncGenerator<UIMessageChunk>\n\nexport interface InProcessTransportOptions {\n run: InProcessRunner\n}\n\n/** Bridge an `AsyncGenerator<UIMessageChunk>` into a pull-based `ReadableStream<UIMessageChunk>`. */\nfunction generatorToStream(gen: AsyncGenerator<UIMessageChunk>): ReadableStream<UIMessageChunk> {\n return new ReadableStream<UIMessageChunk>({\n async pull(controller) {\n try {\n const result = await gen.next()\n if (result.done === true) {\n controller.close()\n return\n }\n // After the done-guard, `result` is an IteratorYieldResult<UIMessageChunk> — value is typed.\n controller.enqueue(result.value)\n } catch (err) {\n controller.error(err)\n }\n },\n async cancel() {\n await gen.return(undefined)\n },\n })\n}\n\n/**\n * M41 (ADR-0050 D4) — `ChatTransport` over the in-process seam (`streamAgentTurnInProcess`), for the\n * terminal/desktop surfaces that run client + server in ONE process (no HTTP loopback).\n *\n * - `sendMessages`: bridge the injected runner's `AsyncGenerator<UIMessageChunk>` into a\n * `ReadableStream<UIMessageChunk>` (honoring `abortSignal`, which the runner forwards to the SDK).\n * - `reconnectToStream`: always `null` — a single process has no dropped server-side stream to resume\n * (mirrors `ai`'s `DirectChatTransport`).\n * - `approve`: resolve the pending inline approval by id (the run parks on `awaitApproval`). An unknown\n * id rejects (fail-fast, Rule 8 — never a silent resolve).\n *\n * Error asymmetry vs `HttpTransport` (by design, matching the `ChatTransport` contract): a runner that\n * throws SYNCHRONOUSLY surfaces the error when the stream is READ (via `controller.error`), not from the\n * `sendMessages` promise — whereas `HttpTransport` throws from `sendMessages` on a non-2xx response.\n */\nexport class InProcessTransport implements AgentTransport {\n readonly #run: InProcessRunner\n /** Pending inline approvals: approvalId → resolver of the parked `awaitApproval` promise. */\n readonly #pending = new Map<string, (decision: boolean | ApprovalDecision) => void>()\n\n constructor(options: InProcessTransportOptions) {\n this.#run = options.run\n }\n\n #awaitApproval: InProcessAwaitApproval = (req) =>\n new Promise<boolean | ApprovalDecision>((resolve, reject) => {\n // Approval ids are server-minted UUIDs — a collision means a real bug (two turns reusing an id).\n // Fail fast rather than silently overwrite the earlier turn's parked resolver (Rule 8).\n if (this.#pending.has(req.approvalId)) {\n reject(new Error(`Duplicate pending approval id '${req.approvalId}' — ids must be unique.`))\n return\n }\n this.#pending.set(req.approvalId, resolve)\n })\n\n sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, metadata } = options\n const generator = this.#run({\n message: extractLastUserText(messages),\n signal: abortSignal ?? undefined,\n awaitApproval: this.#awaitApproval,\n // M43 — forward per-request context (the seam's `metadata`) to the runner.\n context: metadata,\n })\n return Promise.resolve(generatorToStream(generator))\n }\n\n reconnectToStream(): Promise<ReadableStream<UIMessageChunk> | null> {\n return Promise.resolve(null)\n }\n\n approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n const resolve = this.#pending.get(approvalId)\n if (resolve === undefined) {\n return Promise.reject(\n new Error(`No pending approval '${approvalId}' (unknown or already settled).`),\n )\n }\n this.#pending.delete(approvalId)\n resolve(decision)\n return Promise.resolve()\n }\n}\n","import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\nimport { extractLastUserText } from './last-user-text.js'\nimport type { AgentTransport, ApprovalDecision } from './transport.js'\n\n/** Handlers the transport hands to the injected push source for one turn. */\nexport interface ChannelTurnHandlers {\n /** One pushed JSONL line (a serialized `UIMessageChunk`). */\n onLine: (line: string) => void\n /** The turn ended — no more lines. */\n onClose: () => void\n /** The push source failed. */\n onError?: (err: unknown) => void\n}\n\n/**\n * The injected push source — a Tauri `Channel`/`invoke` bridge, kept STRUCTURAL so core adds no\n * `@tauri-apps/*` dependency and the transport is testable with a fake (ADR-0051 D2). The Tauri app\n * wires it: `new Channel()`, `channel.onmessage = onLine`, `invoke('run_agent', { message, channel })`,\n * returning a teardown that aborts the sidecar turn.\n */\nexport interface ChannelPushSource {\n /**\n * Start a turn; deliver each JSONL `UIMessageChunk` line to `onLine`, then `onClose`. Return a teardown.\n * `turn.context` (M43) is the per-request context (from the seam's `metadata`) — the Tauri `invoke`\n * forwards it to the sidecar. When no context is set it is present as `context: undefined` (the key is\n * NOT absent) — a sidecar that checks `'context' in turn` should treat `undefined` as \"no context\".\n */\n start(turn: { message: string; context?: unknown }, handlers: ChannelTurnHandlers): () => void\n /** Optional HITL settle (another Tauri `invoke`). */\n settle?(approvalId: string, decision: ApprovalDecision): Promise<void>\n}\n\nexport interface ChannelTransportOptions {\n source: ChannelPushSource\n}\n\n/**\n * M42 (ADR-0051) — `ChatTransport` over a Tauri-`Channel`-shaped push source, for the desktop webview.\n *\n * - `sendMessages`: start the turn via the injected source and bridge its pushed JSONL frames into a\n * `ReadableStream<UIMessageChunk>` (built in `start` — a Channel is push, so the stream's queue buffers\n * frames; ADR-0051 D3). A malformed JSONL line is SKIPPED, never fatal (ADR-0051 D4, Rule 8). `abortSignal`\n * tears down the source and closes the stream.\n * - `reconnectToStream`: always `null` — the M36 sidecar runs the turn directly (no durable server stream);\n * this is the honest parity for a single-process push surface (ADR-0051 D5; mirrors `InProcessTransport`).\n * - `approve`: routes to the injected `settle` (another Tauri `invoke`); absent `settle` → a typed error.\n *\n * The push source is INJECTED — core stays Tauri-agnostic and this transport is unit-tested with a fake.\n */\nexport class ChannelTransport implements AgentTransport {\n readonly #source: ChannelPushSource\n\n constructor(options: ChannelTransportOptions) {\n this.#source = options.source\n }\n\n sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const { messages, abortSignal, metadata } = options\n const message = extractLastUserText(messages)\n const source = this.#source\n\n // Per-stream teardown/abort-detach, hoisted so `cancel` (consumer stops reading) can also tear the\n // source down. `closed` makes every terminal transition idempotent (no enqueue/close after close).\n let closed = false\n let teardown: () => void = () => undefined\n let detachAbort: () => void = () => undefined\n\n const stream = new ReadableStream<UIMessageChunk>({\n start(controller) {\n const finish = (settle: () => void): void => {\n if (closed) return\n closed = true\n detachAbort()\n settle()\n }\n teardown = source.start(\n { message, context: metadata },\n {\n onLine: (line) => {\n if (closed) return\n // Skip a malformed pushed line — one bad frame must never crash the webview (ADR-0051 D4).\n let parsed: unknown\n try {\n parsed = JSON.parse(line)\n } catch {\n return\n }\n // Discriminant guard: a `UIMessageChunk` is an object with a string `type`. The trust\n // boundary here is the LOCAL sidecar (not the network), so a discriminant check — not the\n // full `ai` schema (which isn't exposed standalone) — is proportionate: it rejects\n // structureless / wrong-shape payloads before they reach `readUIMessageStream`. Same\n // skip-not-crash policy as a parse error.\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n typeof (parsed as { type?: unknown }).type !== 'string'\n ) {\n return\n }\n controller.enqueue(parsed as UIMessageChunk)\n },\n onClose: () => {\n finish(() => {\n controller.close()\n })\n },\n onError: (err) => {\n finish(() => {\n controller.error(err)\n })\n },\n },\n )\n if (abortSignal !== undefined) {\n const onAbort = (): void => {\n finish(() => {\n teardown()\n controller.close()\n })\n }\n if (abortSignal.aborted) onAbort()\n else {\n abortSignal.addEventListener('abort', onAbort)\n detachAbort = () => {\n abortSignal.removeEventListener('abort', onAbort)\n }\n }\n }\n },\n cancel() {\n // The consumer stopped reading (reader.cancel) — abort the source turn + drop the abort listener.\n if (closed) return\n closed = true\n detachAbort()\n teardown()\n },\n })\n return Promise.resolve(stream)\n }\n\n reconnectToStream(): Promise<ReadableStream<UIMessageChunk> | null> {\n return Promise.resolve(null)\n }\n\n async approve(approvalId: string, decision: ApprovalDecision): Promise<void> {\n if (this.#source.settle === undefined) {\n throw new Error(`This channel source has no HITL settle — cannot approve '${approvalId}'.`)\n }\n await this.#source.settle(approvalId, decision)\n }\n}\n","import type { UIMessage } from 'ai'\n\nimport { AgentClient, type AgentClientState } from './agent-client.js'\nimport type { AgentTransport, ApprovalDecision, RequestContext } from './transport.js'\n\nexport interface CreateAgentClientOptions {\n /**\n * M43 — per-request context attached to every turn (`headers` → HTTP; `metadata` → in-process/channel).\n * A value OR a resolver. **To keep context live across sends, pass a RESOLVER** (`() => ({...})`) — a\n * plain value is captured at construction and not re-read, so mutating `options.context` afterwards has\n * no effect (unlike `useAgent`, which reads a live ref). A resolver is re-evaluated every send/reconnect.\n */\n context?: RequestContext | (() => RequestContext | undefined)\n}\n\n/**\n * M44 (ADR-0053) — the standalone (no-React) agent client. A plain handle over the framework-agnostic\n * {@link AgentClient} store: drive a turn, observe state, settle HITL — from a node script, a CLI, a\n * test, or a non-React UI, over ANY {@link AgentTransport}.\n */\nexport interface AgentClientHandle<TInput = unknown> {\n /** Send a typed input; opens a fresh stream (replaces prior messages). */\n send(input: TInput): void\n /** Abort an in-flight stream. */\n abort(): void\n /** Clear messages + error, back to idle. */\n reset(): void\n /** Settle a paused HITL approval via the transport's HITL path. */\n approve(approvalId: string, decision: ApprovalDecision): Promise<void>\n /** Resume an interrupted stream (no-op when the transport can't). */\n reconnect(): void\n /** Subscribe to state changes; returns an unsubscribe fn. */\n subscribe(listener: () => void): () => void\n /**\n * The current immutable snapshot: per-turn `messages` (back-compat) + the full conversation `thread`\n * (M46 — committed history + in-flight, accumulated across sends) + `status` / `error`. A TUI / vanilla\n * consumer renders `thread` directly without hand-rolling a transcript.\n */\n getState(): AgentClientState\n /**\n * Drive one turn and yield the assistant `UIMessage` as it streams (progressive snapshots); iteration\n * ends when the turn settles. A `for await` script takes the last yielded value as the final result;\n * a failed turn rejects the iterator with the run error.\n */\n stream(input: TInput): AsyncIterable<UIMessage>\n}\n\n/** Drive a turn on the store and yield the latest assistant message on each streaming update. */\nfunction streamTurn<TInput>(client: AgentClient<TInput>, input: TInput): AsyncIterable<UIMessage> {\n return {\n async *[Symbol.asyncIterator]() {\n const pending: UIMessage[] = []\n let finished = false\n let streamError: Error | undefined\n let notify: (() => void) | null = null\n // Read via functions — `finished`/`streamError` flip ASYNC in the subscribe callback, so a direct\n // read would be wrongly narrowed to its initial literal by control-flow analysis (M41 pattern).\n const isFinished = (): boolean => finished\n const takeError = (): Error | undefined => streamError\n\n const unsubscribe = client.subscribe(() => {\n const snapshot = client.getSnapshot()\n if (snapshot.status === 'streaming') {\n // Push the growing assistant message ONLY on streaming updates. The terminal ('done'/'error')\n // emit carries the SAME final message already yielded on the last streaming update — pushing it\n // again would yield a duplicate. So the terminal emit only flips `finished` (+ captures error).\n const last = snapshot.messages.at(-1)\n if (last !== undefined) pending.push(last)\n } else {\n if (snapshot.status === 'error') streamError = snapshot.error\n finished = true\n }\n notify?.()\n })\n\n client.send(input)\n try {\n for (;;) {\n while (pending.length > 0) {\n const message = pending.shift()\n if (message !== undefined) yield message\n }\n if (isFinished()) break\n await new Promise<void>((resolve) => {\n notify = resolve\n // Close the lost-wakeup window: an emit may have fired during the drain, BEFORE `notify` was\n // set (its `notify?.()` was then a no-op). If work already arrived, resolve now, don't sleep.\n if (pending.length > 0 || isFinished()) resolve()\n })\n notify = null\n }\n const err = takeError()\n if (err !== undefined) throw err\n } finally {\n // Runs on normal completion AND on early `break` (the for-await calls the generator's return()):\n // stop observing, and abort the turn so an early-exiting consumer doesn't leave it running (abort\n // is a no-op once the turn has settled).\n unsubscribe()\n client.abort()\n }\n },\n }\n}\n\n/**\n * Create a standalone (no-React) agent client over a transport. `useAgent` is the React binding over the\n * same store; this is the equivalent for any JS runtime — one agent, consumable everywhere on the seam.\n */\nexport function createAgentClient<TInput = unknown>(\n transport: AgentTransport,\n options: CreateAgentClientOptions = {},\n): AgentClientHandle<TInput> {\n const { context } = options\n const contextResolver =\n context === undefined\n ? undefined\n : (): RequestContext | undefined => (typeof context === 'function' ? context() : context)\n const client = new AgentClient<TInput>(transport, contextResolver)\n\n return {\n send: client.send,\n abort: client.abort,\n reset: client.reset,\n approve: client.approve,\n reconnect: client.reconnect,\n subscribe: client.subscribe,\n getState: client.getSnapshot,\n stream: (input: TInput) => streamTurn(client, input),\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,10 +5,13 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
broadcastToDevtools
|
|
7
7
|
} from "./chunk-7BIM27LV.js";
|
|
8
|
+
import {
|
|
9
|
+
dispatchControllerRequest
|
|
10
|
+
} from "./chunk-QNFVYST4.js";
|
|
8
11
|
import {
|
|
9
12
|
loadEnv,
|
|
10
13
|
resolveTransformer
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GW5H4KB4.js";
|
|
12
15
|
import {
|
|
13
16
|
BATCH_PATH,
|
|
14
17
|
TRACE_HEADER,
|
|
@@ -17,9 +20,10 @@ import {
|
|
|
17
20
|
executeRoute,
|
|
18
21
|
extractTraceId,
|
|
19
22
|
handleBatchRequest,
|
|
23
|
+
incomingMessageToHandlerRequest,
|
|
20
24
|
incomingMessageToWebRequest,
|
|
21
25
|
sendError
|
|
22
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-QWBXUZAX.js";
|
|
23
27
|
import {
|
|
24
28
|
findSuggestion
|
|
25
29
|
} from "./chunk-5QW7IQQU.js";
|
|
@@ -66,7 +70,7 @@ import {
|
|
|
66
70
|
|
|
67
71
|
// src/vite-plugin/index.ts
|
|
68
72
|
import { existsSync as existsSync9 } from "fs";
|
|
69
|
-
import { resolve as
|
|
73
|
+
import { resolve as resolve10, dirname as dirname4 } from "path";
|
|
70
74
|
import { fileURLToPath } from "url";
|
|
71
75
|
|
|
72
76
|
// src/vite-plugin/config-hook.ts
|
|
@@ -89,15 +93,23 @@ function runConfigHook(ctx) {
|
|
|
89
93
|
server: {
|
|
90
94
|
...warmupClientFiles.length > 0 ? { warmup: { clientFiles: warmupClientFiles } } : {},
|
|
91
95
|
...servicesProxy !== void 0 ? { proxy: servicesProxy } : {},
|
|
92
|
-
// Skip watching gitignored heavy dirs to avoid
|
|
93
|
-
//
|
|
96
|
+
// Skip watching gitignored heavy dirs to avoid ENOSPC (inotify watcher
|
|
97
|
+
// exhaustion), theokit's own generated output, and local SQLite data.
|
|
98
|
+
// #121: a DB write (`.data/app.db` + `-wal`/`-shm`) on every agent turn /
|
|
99
|
+
// request must NOT trigger a full page reload — that "blinks" the screen
|
|
100
|
+
// and tears down the in-flight agent stream.
|
|
94
101
|
watch: {
|
|
95
102
|
ignored: [
|
|
96
103
|
"**/referencias/**",
|
|
97
104
|
"**/.theokit/**",
|
|
98
|
-
"**/.theokit/**",
|
|
99
105
|
"**/dist/**",
|
|
100
|
-
"**/node_modules/**"
|
|
106
|
+
"**/node_modules/**",
|
|
107
|
+
"**/.data/**",
|
|
108
|
+
"**/*.db",
|
|
109
|
+
"**/*.db-wal",
|
|
110
|
+
"**/*.db-shm",
|
|
111
|
+
"**/*.sqlite",
|
|
112
|
+
"**/*.sqlite-journal"
|
|
101
113
|
]
|
|
102
114
|
}
|
|
103
115
|
},
|
|
@@ -759,7 +771,7 @@ async function resolvePluginConfig(projectRoot) {
|
|
|
759
771
|
}
|
|
760
772
|
|
|
761
773
|
// src/vite-plugin/configure-server-hook.ts
|
|
762
|
-
import { resolve as
|
|
774
|
+
import { resolve as resolve7, basename } from "path";
|
|
763
775
|
|
|
764
776
|
// src/devtools/server-side/route-manifest.ts
|
|
765
777
|
function buildPath(parents, segment) {
|
|
@@ -1047,13 +1059,13 @@ function createInProcessApprovalRegistry() {
|
|
|
1047
1059
|
const pending = /* @__PURE__ */ new Map();
|
|
1048
1060
|
return {
|
|
1049
1061
|
register(approvalId, opts) {
|
|
1050
|
-
return new Promise((
|
|
1062
|
+
return new Promise((resolve11) => {
|
|
1051
1063
|
const settle = (decision) => {
|
|
1052
1064
|
const entry = pending.get(approvalId);
|
|
1053
1065
|
if (!entry) return;
|
|
1054
1066
|
clearTimeout(entry.timer);
|
|
1055
1067
|
pending.delete(approvalId);
|
|
1056
|
-
|
|
1068
|
+
resolve11(decision);
|
|
1057
1069
|
};
|
|
1058
1070
|
const timer = setTimeout(() => {
|
|
1059
1071
|
settle({ approved: opts.onTimeout === "proceed" });
|
|
@@ -1946,6 +1958,7 @@ function createAgentMiddleware(vite, projectRoot, csrfMode = "strict", agentsDir
|
|
|
1946
1958
|
}
|
|
1947
1959
|
|
|
1948
1960
|
// src/vite-plugin/api-middleware.ts
|
|
1961
|
+
import { resolve as resolve4 } from "path";
|
|
1949
1962
|
function shouldBypassApiMiddleware(url, prefixes) {
|
|
1950
1963
|
if (!url.startsWith("/api/")) return true;
|
|
1951
1964
|
if (prefixes.length === 0) return false;
|
|
@@ -1993,10 +2006,10 @@ async function handleBatchIfMatch(req, res, ctx) {
|
|
|
1993
2006
|
async function handleBatchInline(req, res, batching, requestId) {
|
|
1994
2007
|
try {
|
|
1995
2008
|
const chunks = [];
|
|
1996
|
-
await new Promise((
|
|
2009
|
+
await new Promise((resolve11, reject) => {
|
|
1997
2010
|
req.on("data", (c) => chunks.push(c));
|
|
1998
2011
|
req.on("end", () => {
|
|
1999
|
-
|
|
2012
|
+
resolve11();
|
|
2000
2013
|
});
|
|
2001
2014
|
req.on("error", reject);
|
|
2002
2015
|
});
|
|
@@ -2024,7 +2037,8 @@ async function runPluginsBeforeRouteMatch(pluginRunner, reqCtx) {
|
|
|
2024
2037
|
const { req, res, requestId, start, url } = reqCtx;
|
|
2025
2038
|
try {
|
|
2026
2039
|
const hookResult = await pluginRunner.runOnRequest({
|
|
2027
|
-
|
|
2040
|
+
// #119 — plugin hooks see a Web `Request` in every runtime (ADR-0028 R3a).
|
|
2041
|
+
request: incomingMessageToHandlerRequest(req),
|
|
2028
2042
|
response: res,
|
|
2029
2043
|
ctx: {},
|
|
2030
2044
|
requestId
|
|
@@ -2049,6 +2063,30 @@ async function runPluginsBeforeRouteMatch(pluginRunner, reqCtx) {
|
|
|
2049
2063
|
}
|
|
2050
2064
|
return false;
|
|
2051
2065
|
}
|
|
2066
|
+
async function tryControllerFallthrough(ctx) {
|
|
2067
|
+
const handled = await dispatchControllerRequest({
|
|
2068
|
+
controllersDir: resolve4(ctx.serverDir, "controllers"),
|
|
2069
|
+
loadModule: ctx.loadModule,
|
|
2070
|
+
req: ctx.req,
|
|
2071
|
+
res: ctx.res,
|
|
2072
|
+
csrfMode: ctx.csrfMode,
|
|
2073
|
+
disallowed: ctx.disallowed,
|
|
2074
|
+
requestId: ctx.requestId
|
|
2075
|
+
});
|
|
2076
|
+
if (!handled) return false;
|
|
2077
|
+
logRequest(
|
|
2078
|
+
{
|
|
2079
|
+
method: (ctx.req.method ?? "GET").toUpperCase(),
|
|
2080
|
+
url: ctx.url,
|
|
2081
|
+
status: ctx.res.statusCode,
|
|
2082
|
+
duration: Date.now() - ctx.start,
|
|
2083
|
+
requestId: ctx.requestId
|
|
2084
|
+
},
|
|
2085
|
+
void 0,
|
|
2086
|
+
ctx.req
|
|
2087
|
+
);
|
|
2088
|
+
return true;
|
|
2089
|
+
}
|
|
2052
2090
|
function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
2053
2091
|
const opts = rateLimitConfigOrOptions && "windowMs" in rateLimitConfigOrOptions ? { rateLimitConfig: rateLimitConfigOrOptions } : rateLimitConfigOrOptions ?? {};
|
|
2054
2092
|
const loadModule = createViteLoader(vite);
|
|
@@ -2099,6 +2137,19 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
|
2099
2137
|
const routes = scanServerRoutes(serverDir);
|
|
2100
2138
|
const match = matchRoute(url, routes);
|
|
2101
2139
|
if (!match) {
|
|
2140
|
+
if (await tryControllerFallthrough({
|
|
2141
|
+
req,
|
|
2142
|
+
res,
|
|
2143
|
+
serverDir,
|
|
2144
|
+
loadModule,
|
|
2145
|
+
csrfMode,
|
|
2146
|
+
disallowed,
|
|
2147
|
+
requestId,
|
|
2148
|
+
url,
|
|
2149
|
+
start
|
|
2150
|
+
})) {
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2102
2153
|
const urlPath = url.split("?")[0];
|
|
2103
2154
|
const routePaths = routes.map((r) => r.routePath);
|
|
2104
2155
|
const suggestion = findSuggestion(urlPath, routePaths);
|
|
@@ -2144,7 +2195,7 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
|
2144
2195
|
|
|
2145
2196
|
// src/vite-plugin/ssr-dev-middleware.ts
|
|
2146
2197
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2147
|
-
import { resolve as
|
|
2198
|
+
import { resolve as resolve5 } from "path";
|
|
2148
2199
|
function isSsrRenderResult(value) {
|
|
2149
2200
|
if (typeof value !== "object" || value === null) return false;
|
|
2150
2201
|
if (!("html" in value)) return false;
|
|
@@ -2159,7 +2210,7 @@ function setupSsrDevMiddleware(server, opts) {
|
|
|
2159
2210
|
return;
|
|
2160
2211
|
}
|
|
2161
2212
|
try {
|
|
2162
|
-
const indexPath =
|
|
2213
|
+
const indexPath = resolve5(opts.projectRoot, "index.html");
|
|
2163
2214
|
let template = readFileSync2(indexPath, "utf-8");
|
|
2164
2215
|
template = await server.transformIndexHtml(url, template);
|
|
2165
2216
|
const nonce = generateNonce();
|
|
@@ -2210,9 +2261,9 @@ function setupSsrDevMiddleware(server, opts) {
|
|
|
2210
2261
|
}
|
|
2211
2262
|
|
|
2212
2263
|
// src/vite-plugin/ws-upgrade.ts
|
|
2213
|
-
import { resolve as
|
|
2264
|
+
import { resolve as resolve6 } from "path";
|
|
2214
2265
|
function setupWsUpgrade(server, projectRoot) {
|
|
2215
|
-
const wsRoutes = scanWebSocketRoutes(
|
|
2266
|
+
const wsRoutes = scanWebSocketRoutes(resolve6(projectRoot, "server"));
|
|
2216
2267
|
if (wsRoutes.length === 0) return;
|
|
2217
2268
|
const wsHttpServer = server.httpServer;
|
|
2218
2269
|
if (!wsHttpServer) return;
|
|
@@ -2294,7 +2345,7 @@ async function runConfigureServer(server, ctx) {
|
|
|
2294
2345
|
csrfReadinessStore
|
|
2295
2346
|
})
|
|
2296
2347
|
);
|
|
2297
|
-
const configPath =
|
|
2348
|
+
const configPath = resolve7(ctx.projectRoot, "theo.config.ts");
|
|
2298
2349
|
server.watcher.on("change", (file) => {
|
|
2299
2350
|
if (file === configPath) {
|
|
2300
2351
|
console.warn(
|
|
@@ -2324,7 +2375,7 @@ async function runConfigureServer(server, ctx) {
|
|
|
2324
2375
|
const openApiCfg = ctx.resolvedOpenApi;
|
|
2325
2376
|
const { reEmitOpenApi } = await import("./dev-emit-2DL3MCLM.js");
|
|
2326
2377
|
const openApiServerDir = ctx.serverDir;
|
|
2327
|
-
const openApiDistDir =
|
|
2378
|
+
const openApiDistDir = resolve7(ctx.projectRoot, ctx.resolvedDistDir);
|
|
2328
2379
|
const isRouteFileForOpenApi = (file) => file.startsWith(openApiServerDir) && /\.(ts|tsx|js|mjs)$/.test(file);
|
|
2329
2380
|
void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
|
|
2330
2381
|
server.watcher.on("change", (file) => {
|
|
@@ -2610,17 +2661,17 @@ async function integrateUseTheoUI(cwd, opts) {
|
|
|
2610
2661
|
|
|
2611
2662
|
// src/vite-plugin/resolve-theo-root.ts
|
|
2612
2663
|
import { existsSync as existsSync7 } from "fs";
|
|
2613
|
-
import { resolve as
|
|
2664
|
+
import { resolve as resolve8 } from "path";
|
|
2614
2665
|
function resolveTheoRootDir(currentDir) {
|
|
2615
|
-
if (existsSync7(
|
|
2666
|
+
if (existsSync7(resolve8(currentDir, "client"))) {
|
|
2616
2667
|
return currentDir;
|
|
2617
2668
|
}
|
|
2618
|
-
return
|
|
2669
|
+
return resolve8(currentDir, "..");
|
|
2619
2670
|
}
|
|
2620
2671
|
|
|
2621
2672
|
// src/vite-plugin/transform-html-hook.ts
|
|
2622
2673
|
import { existsSync as existsSync8 } from "fs";
|
|
2623
|
-
import { resolve as
|
|
2674
|
+
import { resolve as resolve9 } from "path";
|
|
2624
2675
|
|
|
2625
2676
|
// src/vite-plugin/inject-devtools.ts
|
|
2626
2677
|
var DEVTOOLS_VIRTUAL_ID = "/@theo/devtools/entry.js";
|
|
@@ -2720,7 +2771,7 @@ function runTransformIndexHtml(html, ctx) {
|
|
|
2720
2771
|
next = devtools.html;
|
|
2721
2772
|
const styles = injectStylesheets(next, {
|
|
2722
2773
|
isDev: ctx.isDevMode.value,
|
|
2723
|
-
hasPackage: (name) => existsSync8(
|
|
2774
|
+
hasPackage: (name) => existsSync8(resolve9(ctx.projectRoot, "node_modules", ...name.split("/")))
|
|
2724
2775
|
});
|
|
2725
2776
|
next = styles.html;
|
|
2726
2777
|
return next;
|
|
@@ -3280,7 +3331,7 @@ function findConsumerConfig(projectRoot, basename2) {
|
|
|
3280
3331
|
let dir = projectRoot;
|
|
3281
3332
|
for (let level = 0; level < 3; level++) {
|
|
3282
3333
|
for (const ext of extensions) {
|
|
3283
|
-
const candidate =
|
|
3334
|
+
const candidate = resolve10(dir, `${basename2}${ext}`);
|
|
3284
3335
|
if (existsSync9(candidate)) return candidate;
|
|
3285
3336
|
}
|
|
3286
3337
|
const parent = dirname4(dir);
|
|
@@ -3292,7 +3343,7 @@ function findConsumerConfig(projectRoot, basename2) {
|
|
|
3292
3343
|
async function theoPluginAsync(rootOrOptions) {
|
|
3293
3344
|
const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions ?? {};
|
|
3294
3345
|
const projectRoot = options.root ?? process.cwd();
|
|
3295
|
-
const serverDirAbs =
|
|
3346
|
+
const serverDirAbs = resolve10(projectRoot, options.serverDir ?? "server");
|
|
3296
3347
|
const agentsDirName = options.agentsDir ?? "agents";
|
|
3297
3348
|
const consumerTailwindConfig = findConsumerConfig(projectRoot, "tailwind.config");
|
|
3298
3349
|
const consumerPostcssConfig = findConsumerConfig(projectRoot, "postcss.config");
|
|
@@ -3310,23 +3361,23 @@ async function theoPluginAsync(rootOrOptions) {
|
|
|
3310
3361
|
})
|
|
3311
3362
|
);
|
|
3312
3363
|
}
|
|
3313
|
-
const { appTypedClientPlugin } = await import("./app-typed-client-
|
|
3364
|
+
const { appTypedClientPlugin } = await import("./app-typed-client-SPFNGVZL.js");
|
|
3314
3365
|
const appClientPlugin = appTypedClientPlugin({
|
|
3315
3366
|
cwd: projectRoot,
|
|
3316
3367
|
serverDir: serverDirAbs,
|
|
3317
|
-
distDir:
|
|
3368
|
+
distDir: resolve10(projectRoot, ".theokit")
|
|
3318
3369
|
});
|
|
3319
|
-
const { actionsVirtualModule } = await import("./actions-virtual-module-
|
|
3370
|
+
const { actionsVirtualModule } = await import("./actions-virtual-module-H3LY5Q22.js");
|
|
3320
3371
|
const actionsPlugin = actionsVirtualModule({
|
|
3321
3372
|
serverDir: serverDirAbs,
|
|
3322
|
-
distDir:
|
|
3373
|
+
distDir: resolve10(projectRoot, ".theokit")
|
|
3323
3374
|
});
|
|
3324
3375
|
const { agentsTypedClientPlugin } = await import("./agents-typed-client-MAAILRI6.js");
|
|
3325
|
-
const { generateManifest } = await import("./internal-api-
|
|
3376
|
+
const { generateManifest } = await import("./internal-api-MDCX7A7H.js");
|
|
3326
3377
|
const agentsClientPlugin = agentsTypedClientPlugin({
|
|
3327
3378
|
projectRoot,
|
|
3328
3379
|
agentsDir: agentsDirName,
|
|
3329
|
-
distDir:
|
|
3380
|
+
distDir: resolve10(projectRoot, ".theokit"),
|
|
3330
3381
|
// Agents live at <projectRoot>/<agentsDir>; generateManifest scans them via the server dir's
|
|
3331
3382
|
// parent (projectRoot for the canonical layout) + the agents dir name (#95 follow-up).
|
|
3332
3383
|
scanManifest: () => generateManifest(serverDirAbs, projectRoot, agentsDirName)
|
|
@@ -3335,10 +3386,16 @@ async function theoPluginAsync(rootOrOptions) {
|
|
|
3335
3386
|
const routesHmrPlugin = serverRoutesHmrPlugin({
|
|
3336
3387
|
serverDir: serverDirAbs
|
|
3337
3388
|
});
|
|
3389
|
+
const { controllerSwcTransformPlugin } = await import("./controller-swc-transform-URELVCBM.js");
|
|
3390
|
+
const controllerTransformPlugin = controllerSwcTransformPlugin({
|
|
3391
|
+
serverDir: serverDirAbs
|
|
3392
|
+
});
|
|
3338
3393
|
return [
|
|
3339
3394
|
theoPlugin(rootOrOptions),
|
|
3340
3395
|
...uiPlugins,
|
|
3341
3396
|
...servicesPlugins,
|
|
3397
|
+
// #122 — swc-compile controllers/** (enforce:'pre'; order-independent).
|
|
3398
|
+
controllerTransformPlugin,
|
|
3342
3399
|
appClientPlugin,
|
|
3343
3400
|
actionsPlugin,
|
|
3344
3401
|
agentsClientPlugin,
|
|
@@ -3347,10 +3404,10 @@ async function theoPluginAsync(rootOrOptions) {
|
|
|
3347
3404
|
}
|
|
3348
3405
|
function buildOptimizeDepsInclude(projectRoot, viteOptimizeDeps) {
|
|
3349
3406
|
const include = [];
|
|
3350
|
-
if (existsSync9(
|
|
3407
|
+
if (existsSync9(resolve10(projectRoot, "node_modules", "@theokit", "ui"))) {
|
|
3351
3408
|
include.push("@theokit/ui");
|
|
3352
3409
|
}
|
|
3353
|
-
if (existsSync9(
|
|
3410
|
+
if (existsSync9(resolve10(projectRoot, "node_modules", "lucide-react"))) {
|
|
3354
3411
|
include.push("lucide-react");
|
|
3355
3412
|
}
|
|
3356
3413
|
include.push("devalue");
|
|
@@ -3376,8 +3433,8 @@ var VIRTUAL_MODULE_IDS = {
|
|
|
3376
3433
|
function theoPlugin(rootOrOptions) {
|
|
3377
3434
|
const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions ?? {};
|
|
3378
3435
|
const projectRoot = options.root ?? process.cwd();
|
|
3379
|
-
const appDir =
|
|
3380
|
-
const serverDir =
|
|
3436
|
+
const appDir = resolve10(projectRoot, options.appDir ?? "app");
|
|
3437
|
+
const serverDir = resolve10(projectRoot, options.serverDir ?? "server");
|
|
3381
3438
|
const agentsDir = options.agentsDir ?? "agents";
|
|
3382
3439
|
const ssrEnabled = options.ssr ?? false;
|
|
3383
3440
|
const currentDir = dirname4(fileURLToPath(import.meta.url));
|
|
@@ -3505,4 +3562,4 @@ export {
|
|
|
3505
3562
|
theoPluginAsync,
|
|
3506
3563
|
theoPlugin
|
|
3507
3564
|
};
|
|
3508
|
-
//# sourceMappingURL=chunk-
|
|
3565
|
+
//# sourceMappingURL=chunk-QCB7ZNR2.js.map
|