@percena/weft 0.4.0-next.3 → 0.4.0-next.5

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.
Files changed (81) hide show
  1. package/dist/action-bridge.cjs +172 -6
  2. package/dist/action-bridge.d.cts +112 -13
  3. package/dist/action-bridge.d.ts +112 -13
  4. package/dist/action-bridge.js +166 -4
  5. package/dist/chat.cjs +982 -3
  6. package/dist/chat.d.cts +30 -1
  7. package/dist/chat.d.ts +30 -1
  8. package/dist/chat.js +981 -2
  9. package/dist/index.cjs +341 -1616
  10. package/dist/index.d.cts +4 -274
  11. package/dist/index.d.ts +4 -274
  12. package/dist/index.js +174 -1408
  13. package/dist/styles/fonts/KaTeX_AMS-Regular.ttf +0 -0
  14. package/dist/styles/fonts/KaTeX_AMS-Regular.woff +0 -0
  15. package/dist/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  16. package/dist/styles/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  17. package/dist/styles/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  18. package/dist/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  19. package/dist/styles/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  20. package/dist/styles/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  21. package/dist/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  22. package/dist/styles/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  23. package/dist/styles/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  24. package/dist/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  25. package/dist/styles/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  26. package/dist/styles/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  27. package/dist/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  28. package/dist/styles/fonts/KaTeX_Main-Bold.ttf +0 -0
  29. package/dist/styles/fonts/KaTeX_Main-Bold.woff +0 -0
  30. package/dist/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
  31. package/dist/styles/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  32. package/dist/styles/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  33. package/dist/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  34. package/dist/styles/fonts/KaTeX_Main-Italic.ttf +0 -0
  35. package/dist/styles/fonts/KaTeX_Main-Italic.woff +0 -0
  36. package/dist/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
  37. package/dist/styles/fonts/KaTeX_Main-Regular.ttf +0 -0
  38. package/dist/styles/fonts/KaTeX_Main-Regular.woff +0 -0
  39. package/dist/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
  40. package/dist/styles/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  41. package/dist/styles/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  42. package/dist/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  43. package/dist/styles/fonts/KaTeX_Math-Italic.ttf +0 -0
  44. package/dist/styles/fonts/KaTeX_Math-Italic.woff +0 -0
  45. package/dist/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
  46. package/dist/styles/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  47. package/dist/styles/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  48. package/dist/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  49. package/dist/styles/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  50. package/dist/styles/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  51. package/dist/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  52. package/dist/styles/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  53. package/dist/styles/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  54. package/dist/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  55. package/dist/styles/fonts/KaTeX_Script-Regular.ttf +0 -0
  56. package/dist/styles/fonts/KaTeX_Script-Regular.woff +0 -0
  57. package/dist/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
  58. package/dist/styles/fonts/KaTeX_Size1-Regular.ttf +0 -0
  59. package/dist/styles/fonts/KaTeX_Size1-Regular.woff +0 -0
  60. package/dist/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  61. package/dist/styles/fonts/KaTeX_Size2-Regular.ttf +0 -0
  62. package/dist/styles/fonts/KaTeX_Size2-Regular.woff +0 -0
  63. package/dist/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  64. package/dist/styles/fonts/KaTeX_Size3-Regular.ttf +0 -0
  65. package/dist/styles/fonts/KaTeX_Size3-Regular.woff +0 -0
  66. package/dist/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  67. package/dist/styles/fonts/KaTeX_Size4-Regular.ttf +0 -0
  68. package/dist/styles/fonts/KaTeX_Size4-Regular.woff +0 -0
  69. package/dist/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  70. package/dist/styles/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  71. package/dist/styles/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  72. package/dist/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  73. package/package.json +2 -30
  74. package/dist/local-runtime.cjs +0 -1387
  75. package/dist/local-runtime.d.cts +0 -3314
  76. package/dist/local-runtime.d.ts +0 -3314
  77. package/dist/local-runtime.js +0 -1345
  78. package/dist/skills-browser.cjs +0 -118
  79. package/dist/skills-browser.d.cts +0 -105
  80. package/dist/skills-browser.d.ts +0 -105
  81. package/dist/skills-browser.js +0 -88
package/dist/chat.js CHANGED
@@ -7043,6 +7043,27 @@ import { useState as useState22 } from "react";
7043
7043
  import { useCallback as useCallback9, useEffect as useEffect9, useMemo as useMemo8, useRef as useRef8, useState as useState12 } from "react";
7044
7044
 
7045
7045
  // ../packages/timeline/dist/index.js
7046
+ function createTimelineCursor(cursor) {
7047
+ return {
7048
+ epoch: cursor.epoch,
7049
+ afterSeq: cursor.afterSeq
7050
+ };
7051
+ }
7052
+ function fetchTimeline(timeline, request = {}) {
7053
+ const ordered = sortTimeline(timeline);
7054
+ const cursor = request.cursor ?? cursorFromTimelineStart(ordered);
7055
+ const items = ordered.filter((item) => item.epoch === cursor.epoch && item.seq > cursor.afterSeq).slice(0, request.limit);
7056
+ const lastSeq = items.at(-1)?.seq ?? cursor.afterSeq;
7057
+ const firstSeq = items[0]?.seq;
7058
+ return {
7059
+ items,
7060
+ nextCursor: {
7061
+ epoch: cursor.epoch,
7062
+ afterSeq: lastSeq
7063
+ },
7064
+ hasGap: firstSeq !== void 0 && firstSeq > cursor.afterSeq + 1
7065
+ };
7066
+ }
7046
7067
  function mergeTimeline(existing, incoming) {
7047
7068
  const byKey = /* @__PURE__ */ new Map();
7048
7069
  for (const item of [...existing, ...incoming]) {
@@ -7050,6 +7071,12 @@ function mergeTimeline(existing, incoming) {
7050
7071
  }
7051
7072
  return sortTimeline([...byKey.values()]);
7052
7073
  }
7074
+ function cursorFromTimelineStart(timeline) {
7075
+ return {
7076
+ epoch: timeline[0]?.epoch ?? "default",
7077
+ afterSeq: 0
7078
+ };
7079
+ }
7053
7080
  function sortTimeline(timeline) {
7054
7081
  return [...timeline].sort((left, right) => {
7055
7082
  if (left.epoch !== right.epoch) return left.epoch.localeCompare(right.epoch);
@@ -7823,8 +7850,960 @@ function TimelineAgentChatPanel({
7823
7850
  ] });
7824
7851
  }
7825
7852
 
7826
- // src/chat.ts
7827
- import { useAgentSession } from "@percena/weft-react";
7853
+ // ../packages/react/src/use-agent-session.ts
7854
+ import { useEffect as useEffect10, useMemo as useMemo9, useRef as useRef9 } from "react";
7855
+
7856
+ // ../packages/runtime-core/dist/index.js
7857
+ var RUNTIME_KINDS = ["native-sdk", "app-server", "compatible-sdk", "cli-fallback"];
7858
+ var initialRuntimeState = {
7859
+ status: "idle",
7860
+ acceptedMessages: [],
7861
+ queuedMessages: []
7862
+ };
7863
+ function reduceRuntimeState(state = initialRuntimeState, action) {
7864
+ switch (action.type) {
7865
+ case "preflight_start":
7866
+ return { ...state, status: "preflighting", lastError: void 0 };
7867
+ case "preflight_ok":
7868
+ return { ...state, status: "ready", lastError: void 0 };
7869
+ case "preflight_error":
7870
+ return { ...state, status: "failed", lastError: action.error };
7871
+ case "starting":
7872
+ if (state.status === "ready" || state.status === "idle") {
7873
+ return { ...state, status: "starting" };
7874
+ }
7875
+ return state;
7876
+ case "send_message":
7877
+ if (state.status === "running" || state.status === "waiting_for_permission") {
7878
+ return {
7879
+ ...state,
7880
+ queuedMessages: [...state.queuedMessages, action.message]
7881
+ };
7882
+ }
7883
+ return {
7884
+ ...state,
7885
+ status: "running",
7886
+ acceptedMessages: [...state.acceptedMessages, action.message]
7887
+ };
7888
+ case "permission_request":
7889
+ return {
7890
+ ...state,
7891
+ status: "waiting_for_permission",
7892
+ waitingPermissionRequestId: action.requestId
7893
+ };
7894
+ case "permission_response":
7895
+ return {
7896
+ ...state,
7897
+ status: "running",
7898
+ waitingPermissionRequestId: void 0
7899
+ };
7900
+ case "turn_completed":
7901
+ if (state.status === "running") {
7902
+ return { ...state, status: "turn_completed" };
7903
+ }
7904
+ return state;
7905
+ case "complete": {
7906
+ if (state.status === "turn_completed") {
7907
+ const [nextMessage2, ...remaining2] = state.queuedMessages;
7908
+ if (nextMessage2) {
7909
+ return {
7910
+ ...state,
7911
+ status: "running",
7912
+ acceptedMessages: [...state.acceptedMessages, nextMessage2],
7913
+ queuedMessages: remaining2
7914
+ };
7915
+ }
7916
+ return { ...state, status: "ready" };
7917
+ }
7918
+ const [nextMessage, ...remaining] = state.queuedMessages;
7919
+ if (nextMessage) {
7920
+ return {
7921
+ ...state,
7922
+ status: "running",
7923
+ acceptedMessages: [...state.acceptedMessages, nextMessage],
7924
+ queuedMessages: remaining
7925
+ };
7926
+ }
7927
+ return { ...state, status: "ready" };
7928
+ }
7929
+ case "abort":
7930
+ return {
7931
+ ...state,
7932
+ status: "ready",
7933
+ lastError: action.reason,
7934
+ queuedMessages: [],
7935
+ waitingPermissionRequestId: void 0
7936
+ };
7937
+ case "error":
7938
+ return { ...state, status: "failed", lastError: action.error };
7939
+ case "dispose":
7940
+ return {
7941
+ ...state,
7942
+ status: "disposed",
7943
+ queuedMessages: [],
7944
+ waitingPermissionRequestId: void 0
7945
+ };
7946
+ }
7947
+ }
7948
+ function createRuntimeExtensionContext(context = {}) {
7949
+ return {
7950
+ policy: context.policy,
7951
+ sources: context.sources ? { enabledSourceSlugs: unique(context.sources.enabledSourceSlugs) } : void 0,
7952
+ skills: context.skills ? { activeSkillSlugs: unique(context.skills.activeSkillSlugs) } : void 0,
7953
+ commandOrigin: context.commandOrigin,
7954
+ hostServices: context.hostServices
7955
+ };
7956
+ }
7957
+ function unique(values) {
7958
+ return [...new Set(values)];
7959
+ }
7960
+ function findCandidate(candidates, kind) {
7961
+ return candidates.find((candidate) => candidate.kind === kind);
7962
+ }
7963
+ function defaultRuntimeKindOrder(provider) {
7964
+ if (provider === "codex") {
7965
+ return ["app-server", "native-sdk", "compatible-sdk", "cli-fallback"];
7966
+ }
7967
+ return RUNTIME_KINDS;
7968
+ }
7969
+ function firstAvailableCandidate(candidates, provider) {
7970
+ for (const kind of defaultRuntimeKindOrder(provider)) {
7971
+ const candidate = findCandidate(candidates, kind);
7972
+ if (candidate?.available) return candidate;
7973
+ }
7974
+ return void 0;
7975
+ }
7976
+ function selectRuntimeCandidate(options) {
7977
+ const preferred = options.preferredRuntime ? findCandidate(options.candidates, options.preferredRuntime) : void 0;
7978
+ if (preferred?.available) {
7979
+ return { selected: preferred.kind, fallback: false };
7980
+ }
7981
+ if (preferred && options.allowFallback !== true) {
7982
+ return {
7983
+ fallback: false,
7984
+ error: preferred.reason ?? `${preferred.kind} runtime is unavailable`
7985
+ };
7986
+ }
7987
+ const selected = firstAvailableCandidate(options.candidates, options.provider);
7988
+ if (!selected) {
7989
+ return {
7990
+ fallback: false,
7991
+ error: options.candidates.map((candidate) => candidate.reason).filter(Boolean).join("; ") || "No runtime candidates are available"
7992
+ };
7993
+ }
7994
+ const fallback = Boolean(preferred && selected.kind !== preferred.kind);
7995
+ return {
7996
+ selected: selected.kind,
7997
+ fallback,
7998
+ fallbackReason: fallback ? preferred?.reason ?? `${preferred?.kind ?? "preferred"} runtime is unavailable` : void 0
7999
+ };
8000
+ }
8001
+ function createRuntimeCapabilityReport(options) {
8002
+ const selection = selectRuntimeCandidate(options);
8003
+ const extensionCapabilities = normalizeExtensionCapabilities(options.extensionCapabilities);
8004
+ return {
8005
+ provider: options.provider,
8006
+ candidates: options.candidates,
8007
+ preferredRuntime: options.preferredRuntime,
8008
+ allowFallback: options.allowFallback === true,
8009
+ auth: options.auth,
8010
+ ...extensionCapabilities,
8011
+ ...selection
8012
+ };
8013
+ }
8014
+ function normalizeExtensionCapabilities(capabilities = {}) {
8015
+ return {
8016
+ policyCapabilities: capabilities.policy ?? {
8017
+ supported: false,
8018
+ modes: [],
8019
+ approvals: false,
8020
+ toolPolicy: false
8021
+ },
8022
+ sourceCapabilities: capabilities.sources ?? {
8023
+ supported: false
8024
+ },
8025
+ skillCapabilities: capabilities.skills ?? {
8026
+ supported: false
8027
+ },
8028
+ automationCapabilities: capabilities.automations ?? {
8029
+ supported: false,
8030
+ eventBus: false,
8031
+ schedulerHost: false,
8032
+ promptAction: false,
8033
+ webhookAction: false
8034
+ },
8035
+ hostToolCapabilities: capabilities.hostTools ?? {
8036
+ supported: false,
8037
+ sessionTools: false,
8038
+ workflowTransitions: false,
8039
+ browserActions: false,
8040
+ metadataWrites: false
8041
+ }
8042
+ };
8043
+ }
8044
+
8045
+ // ../packages/client/dist/index.js
8046
+ var FlitroHttpClient = class {
8047
+ baseUrl;
8048
+ timeout;
8049
+ apiKey;
8050
+ tenantId;
8051
+ onTokenExpired;
8052
+ token;
8053
+ constructor(options) {
8054
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
8055
+ this.timeout = options.timeout ?? 3e4;
8056
+ this.apiKey = options.apiKey ?? "";
8057
+ this.tenantId = options.tenantId ?? "";
8058
+ this.token = options.token ?? "";
8059
+ this.onTokenExpired = options.onTokenExpired;
8060
+ }
8061
+ /** Current Authorization bearer (scoped token wins over apiKey). */
8062
+ getBearerToken() {
8063
+ return this.token || this.apiKey;
8064
+ }
8065
+ /** Replace the scoped token (e.g. after a host-backend refresh). */
8066
+ setToken(token) {
8067
+ this.token = token;
8068
+ }
8069
+ buildHeaders() {
8070
+ const headers = { "Content-Type": "application/json" };
8071
+ const bearer = this.getBearerToken();
8072
+ if (bearer) headers["Authorization"] = `Bearer ${bearer}`;
8073
+ if (!this.token && this.tenantId) headers["X-Tenant-ID"] = this.tenantId;
8074
+ return headers;
8075
+ }
8076
+ async preflight() {
8077
+ return this.get("/v1/capabilities");
8078
+ }
8079
+ async health() {
8080
+ return this.get("/v1/health");
8081
+ }
8082
+ async createSession(options) {
8083
+ return this.post("/v1/sessions", {
8084
+ title: options?.title ?? "Weft session",
8085
+ model: options?.model,
8086
+ skill_names: options?.skillNames,
8087
+ mcp_server_names: options?.mcpServerNames
8088
+ });
8089
+ }
8090
+ async getSession(sessionId) {
8091
+ return this.get(`/v1/sessions/${encodeURIComponent(sessionId)}`);
8092
+ }
8093
+ async sendMessage(sessionId, message, options) {
8094
+ const budget = options?.budget ? {
8095
+ max_steps: options.budget.maxSteps,
8096
+ max_tokens: options.budget.maxTokens,
8097
+ max_wall_time_sec: options.budget.maxWallTimeSec
8098
+ } : void 0;
8099
+ return this.post(`/v1/sessions/${encodeURIComponent(sessionId)}/runs`, {
8100
+ message,
8101
+ model: options?.model,
8102
+ skill_names: options?.skillNames,
8103
+ mcp_server_names: options?.mcpServerNames,
8104
+ tool_names: options?.toolNames,
8105
+ approval_policy: options?.approvalPolicy,
8106
+ execution_mode: options?.executionMode,
8107
+ workspace_id: options?.workspaceId,
8108
+ budget
8109
+ });
8110
+ }
8111
+ async cancelRun(runId) {
8112
+ return this.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {});
8113
+ }
8114
+ async resumeTool(runId, resumeData) {
8115
+ return this.post(
8116
+ `/v1/runs/${encodeURIComponent(runId)}/resume-tool`,
8117
+ { resume_data: resumeData }
8118
+ );
8119
+ }
8120
+ async respondToPermission(sessionId, requestId, allowed, options) {
8121
+ return this.post(
8122
+ `/v1/sessions/${encodeURIComponent(sessionId)}/permission-response`,
8123
+ { requestId, allowed, remember: options?.remember, text: options?.text, answer: options?.answer }
8124
+ );
8125
+ }
8126
+ async patchSession(sessionId, patch) {
8127
+ return this.patch(
8128
+ `/v1/sessions/${encodeURIComponent(sessionId)}`,
8129
+ patch
8130
+ );
8131
+ }
8132
+ async listModels() {
8133
+ return this.get("/v1/models");
8134
+ }
8135
+ async fetchTimeline(sessionId, afterSeq, limit) {
8136
+ const params = new URLSearchParams();
8137
+ if (afterSeq !== void 0) params.set("after_seq", String(afterSeq));
8138
+ if (limit !== void 0) params.set("limit", String(limit));
8139
+ const qs = params.toString();
8140
+ const url = `/v1/sessions/${encodeURIComponent(sessionId)}/timeline/fetch${qs ? `?${qs}` : ""}`;
8141
+ return this.get(url);
8142
+ }
8143
+ /** Returns the SSE stream URL for a session's canonical timeline. */
8144
+ sessionTimelineUrl(sessionId) {
8145
+ return `${this.baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/timeline`;
8146
+ }
8147
+ async get(path) {
8148
+ return this.request("GET", path, void 0);
8149
+ }
8150
+ async post(path, body) {
8151
+ return this.request("POST", path, body);
8152
+ }
8153
+ async patch(path, body) {
8154
+ return this.request("PATCH", path, body);
8155
+ }
8156
+ async request(method, path, body, isRetry = false) {
8157
+ const url = `${this.baseUrl}${path}`;
8158
+ const controller = new AbortController();
8159
+ const timer = setTimeout(() => controller.abort(), this.timeout);
8160
+ try {
8161
+ const response = await fetch(url, {
8162
+ method,
8163
+ headers: this.buildHeaders(),
8164
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
8165
+ signal: controller.signal
8166
+ });
8167
+ if (response.status === 401 && this.token && this.onTokenExpired && !isRetry) {
8168
+ const fresh = await this.onTokenExpired();
8169
+ if (fresh) {
8170
+ this.token = fresh;
8171
+ return this.request(method, path, body, true);
8172
+ }
8173
+ }
8174
+ if (!response.ok) {
8175
+ let errorMsg = `Flitro HTTP ${response.status}`;
8176
+ try {
8177
+ const errBody = await response.json();
8178
+ if (errBody.error) errorMsg = `${errorMsg}: ${errBody.error}`;
8179
+ } catch {
8180
+ }
8181
+ throw new Error(errorMsg);
8182
+ }
8183
+ return response.json();
8184
+ } finally {
8185
+ clearTimeout(timer);
8186
+ }
8187
+ }
8188
+ };
8189
+ var FlitroFetchSseTimelineStream = class {
8190
+ listeners = /* @__PURE__ */ new Set();
8191
+ abortController = null;
8192
+ connected = false;
8193
+ disposed = false;
8194
+ afterSeq;
8195
+ reconnectAttempts = 0;
8196
+ tokenOverride = "";
8197
+ getBearerToken;
8198
+ onTokenExpired;
8199
+ options;
8200
+ constructor(options) {
8201
+ this.afterSeq = options.initialAfterSeq ?? 0;
8202
+ this.getBearerToken = options.getBearerToken;
8203
+ this.onTokenExpired = options.onTokenExpired;
8204
+ this.options = {
8205
+ url: options.url,
8206
+ apiKey: options.apiKey ?? "",
8207
+ tenantId: options.tenantId ?? "",
8208
+ reconnectDelayMs: options.reconnectDelayMs ?? 1500,
8209
+ maxReconnectAttempts: options.maxReconnectAttempts ?? 20,
8210
+ initialAfterSeq: this.afterSeq,
8211
+ now: options.now ?? (() => Date.now())
8212
+ };
8213
+ }
8214
+ connect(onEvent, onError, onClose) {
8215
+ this.disposed = false;
8216
+ this.listeners.add({ onEvent, onError, onClose });
8217
+ if (!this.connected) {
8218
+ this.connected = true;
8219
+ this.reconnectAttempts = 0;
8220
+ this.startFetch();
8221
+ }
8222
+ }
8223
+ disconnect() {
8224
+ this.disposed = true;
8225
+ this.connected = false;
8226
+ this.abortController?.abort();
8227
+ for (const listener of this.listeners) {
8228
+ listener.onClose?.();
8229
+ }
8230
+ this.listeners.clear();
8231
+ }
8232
+ isConnected() {
8233
+ return this.connected && !this.disposed;
8234
+ }
8235
+ buildUrl() {
8236
+ let url = this.options.url;
8237
+ if (this.afterSeq > 0) {
8238
+ url += (url.includes("?") ? "&" : "?") + `after_seq=${this.afterSeq}`;
8239
+ }
8240
+ return url;
8241
+ }
8242
+ buildHeaders() {
8243
+ const h = { Accept: "text/event-stream" };
8244
+ const bearer = this.tokenOverride || this.getBearerToken?.() || this.options.apiKey;
8245
+ if (bearer) h["Authorization"] = `Bearer ${bearer}`;
8246
+ if (this.options.tenantId) h["X-Tenant-ID"] = this.options.tenantId;
8247
+ return h;
8248
+ }
8249
+ async startFetch() {
8250
+ if (this.disposed) return;
8251
+ this.abortController = new AbortController();
8252
+ try {
8253
+ const response = await fetch(this.buildUrl(), {
8254
+ headers: this.buildHeaders(),
8255
+ signal: this.abortController.signal
8256
+ });
8257
+ if (response.status === 401 && this.onTokenExpired) {
8258
+ const fresh = await this.onTokenExpired();
8259
+ if (fresh) this.tokenOverride = fresh;
8260
+ }
8261
+ if (!response.ok || !response.body) {
8262
+ throw new Error(`Flitro SSE: HTTP ${response.status}`);
8263
+ }
8264
+ this.reconnectAttempts = 0;
8265
+ const reader = response.body.getReader();
8266
+ const decoder = new TextDecoder();
8267
+ let buffer = "";
8268
+ while (true) {
8269
+ const { done, value } = await reader.read();
8270
+ if (done) break;
8271
+ buffer += decoder.decode(value, { stream: true });
8272
+ const envelopes = parseSseBuffer(buffer);
8273
+ buffer = envelopes.remainder;
8274
+ for (const envelope of envelopes.items) {
8275
+ if (envelope.seq > this.afterSeq) {
8276
+ this.afterSeq = envelope.seq;
8277
+ }
8278
+ for (const listener of this.listeners) {
8279
+ listener.onEvent(envelope);
8280
+ }
8281
+ }
8282
+ }
8283
+ if (!this.disposed) {
8284
+ this.connected = false;
8285
+ this.scheduleReconnect();
8286
+ }
8287
+ } catch (err) {
8288
+ if (this.disposed) return;
8289
+ if (err instanceof Error && err.name === "AbortError") return;
8290
+ this.connected = false;
8291
+ this.scheduleReconnect();
8292
+ }
8293
+ }
8294
+ scheduleReconnect() {
8295
+ if (this.disposed) return;
8296
+ if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
8297
+ this.emitError(new Error(`Flitro SSE: max reconnect attempts reached`));
8298
+ return;
8299
+ }
8300
+ this.reconnectAttempts++;
8301
+ const delay = Math.min(this.options.reconnectDelayMs * this.reconnectAttempts, 3e4);
8302
+ setTimeout(() => {
8303
+ this.connected = true;
8304
+ this.startFetch();
8305
+ }, delay);
8306
+ }
8307
+ emitError(err) {
8308
+ for (const listener of this.listeners) listener.onError?.(err);
8309
+ }
8310
+ };
8311
+ function parseSseBuffer(buffer) {
8312
+ const items = [];
8313
+ const blocks = buffer.split("\n\n");
8314
+ const remainder = blocks.pop() ?? "";
8315
+ for (const block of blocks) {
8316
+ let eventType = "";
8317
+ let data = "";
8318
+ for (const line of block.split("\n")) {
8319
+ if (line.startsWith("event: ")) {
8320
+ eventType = line.slice(7).trim();
8321
+ } else if (line.startsWith("data: ")) {
8322
+ data = line.slice(6).trim();
8323
+ }
8324
+ }
8325
+ if (eventType === "timeline" && data) {
8326
+ try {
8327
+ items.push(JSON.parse(data));
8328
+ } catch {
8329
+ }
8330
+ }
8331
+ }
8332
+ return { items, remainder };
8333
+ }
8334
+ function createFlitroTimelineStream(options) {
8335
+ return new FlitroFetchSseTimelineStream(options);
8336
+ }
8337
+
8338
+ // ../packages/provider-flitro/dist/index.js
8339
+ function mapPermissionModeToApprovalPolicy(mode) {
8340
+ if (mode === "allow-all") return "auto";
8341
+ if (mode === "safe") return "never";
8342
+ if (mode === "ask") return "on-request";
8343
+ return void 0;
8344
+ }
8345
+ function createFlitroDriver(options) {
8346
+ let activeRunId;
8347
+ return {
8348
+ async sendMessage(input, _sequencer) {
8349
+ const run = await options.client.sendMessage(
8350
+ options.sessionId,
8351
+ input.message,
8352
+ {
8353
+ model: input.options?.model ?? options.model,
8354
+ skillNames: options.skillNames,
8355
+ mcpServerNames: options.mcpServerNames,
8356
+ // Per-message permission choice (from the chat panel selector) wins
8357
+ // over the runtime's creation-time default.
8358
+ approvalPolicy: mapPermissionModeToApprovalPolicy(input.options?.permissionMode) ?? options.approvalPolicy
8359
+ }
8360
+ );
8361
+ activeRunId = run.run_id;
8362
+ },
8363
+ async abort(reason) {
8364
+ if (activeRunId) {
8365
+ await options.client.cancelRun(activeRunId).catch(() => {
8366
+ });
8367
+ activeRunId = void 0;
8368
+ }
8369
+ },
8370
+ async respondToPermission(requestId, allowed, remember) {
8371
+ await options.client.respondToPermission(options.sessionId, requestId, allowed, {
8372
+ remember
8373
+ });
8374
+ },
8375
+ async resumeTool(runId, resumeData) {
8376
+ await options.client.resumeTool(runId, resumeData);
8377
+ },
8378
+ async dispose() {
8379
+ if (activeRunId) {
8380
+ await options.client.cancelRun(activeRunId).catch(() => {
8381
+ });
8382
+ activeRunId = void 0;
8383
+ }
8384
+ }
8385
+ };
8386
+ }
8387
+ function createFlitroRuntimeCapabilityReport(options) {
8388
+ const appServerAvailable = options.candidates.some((c) => c.kind === "app-server" && c.available);
8389
+ return createRuntimeCapabilityReport({
8390
+ provider: "flitro",
8391
+ candidates: options.candidates,
8392
+ preferredRuntime: "app-server",
8393
+ allowFallback: options.allowFallback,
8394
+ auth: options.auth,
8395
+ extensionCapabilities: {
8396
+ policy: {
8397
+ supported: true,
8398
+ degraded: !appServerAvailable || void 0,
8399
+ modes: ["safe", "ask", "allow-all"],
8400
+ approvals: appServerAvailable,
8401
+ toolPolicy: appServerAvailable,
8402
+ reason: appServerAvailable ? void 0 : "Flitro server is unavailable"
8403
+ },
8404
+ sources: {
8405
+ supported: appServerAvailable,
8406
+ registry: appServerAvailable,
8407
+ mcpTools: appServerAvailable,
8408
+ credentialGateway: false,
8409
+ reason: appServerAvailable ? void 0 : "Source tools require a running Flitro server"
8410
+ },
8411
+ skills: {
8412
+ supported: appServerAvailable,
8413
+ registry: appServerAvailable,
8414
+ activationPlan: appServerAvailable,
8415
+ scopedPolicy: appServerAvailable,
8416
+ reason: appServerAvailable ? void 0 : "Skill activation requires a running Flitro server"
8417
+ },
8418
+ automations: {
8419
+ supported: appServerAvailable,
8420
+ degraded: !appServerAvailable || void 0,
8421
+ eventBus: appServerAvailable,
8422
+ schedulerHost: appServerAvailable,
8423
+ promptAction: appServerAvailable,
8424
+ webhookAction: false,
8425
+ reason: appServerAvailable ? void 0 : "Automations require a running Flitro server"
8426
+ },
8427
+ hostTools: {
8428
+ supported: appServerAvailable,
8429
+ sessionTools: false,
8430
+ workflowTransitions: false,
8431
+ browserActions: false,
8432
+ metadataWrites: appServerAvailable
8433
+ }
8434
+ }
8435
+ });
8436
+ }
8437
+ function createFlitroProviderRuntime(options) {
8438
+ const report = createFlitroRuntimeCapabilityReport(options);
8439
+ const extensions = createRuntimeExtensionContext(options.extensions);
8440
+ const client = new FlitroHttpClient(options.server);
8441
+ let resolvedSessionId = options.sessionId ?? "";
8442
+ let epoch = options.epoch ?? (resolvedSessionId ? `flitro-${resolvedSessionId}` : "flitro-pending");
8443
+ const now = options.now ?? (() => Date.now());
8444
+ const timeline = [];
8445
+ const seenTimelineKeys = /* @__PURE__ */ new Set();
8446
+ const stream = new PushTimelineStream();
8447
+ let state = initialRuntimeState;
8448
+ let runtimeDriver = options.driver;
8449
+ let sseStream;
8450
+ const pendingMessages = [];
8451
+ function dispatch(action) {
8452
+ state = reduceRuntimeState(state, action);
8453
+ }
8454
+ function syncStateFromEnvelope(envelope) {
8455
+ const item = envelope.item;
8456
+ if (item.type === "permission_requested") {
8457
+ dispatch({ type: "permission_request", requestId: item.request.requestId });
8458
+ } else if (item.type === "permission_resolved") {
8459
+ dispatch({ type: "permission_response" });
8460
+ } else if (item.type === "turn_completed") {
8461
+ dispatch({ type: "turn_completed" });
8462
+ dispatch({ type: "complete" });
8463
+ void sendNextPendingMessage();
8464
+ } else if (item.type === "turn_failed") {
8465
+ const err = item.error;
8466
+ dispatch({ type: "error", error: err?.message ?? "turn failed" });
8467
+ } else if (item.type === "tool_suspended") {
8468
+ dispatch({ type: "permission_request", requestId: `tool-suspend:${item.callId}` });
8469
+ } else if (item.type === "tool_resumed") {
8470
+ dispatch({ type: "permission_response" });
8471
+ } else if (item.type === "session_status") {
8472
+ if (item.status === "running") {
8473
+ dispatch({ type: "starting" });
8474
+ } else if (item.status === "ended") {
8475
+ dispatch({ type: "abort" });
8476
+ }
8477
+ }
8478
+ }
8479
+ function appendEnvelope(envelope) {
8480
+ if (envelope.seq > 0) {
8481
+ const key = `${envelope.epoch || envelope.sessionId || "unknown"}:${envelope.seq}`;
8482
+ if (seenTimelineKeys.has(key)) return;
8483
+ seenTimelineKeys.add(key);
8484
+ }
8485
+ syncStateFromEnvelope(envelope);
8486
+ timeline.push(envelope);
8487
+ stream.emit(envelope);
8488
+ }
8489
+ function appendFailure(message) {
8490
+ const item = { type: "turn_failed", turnId: "provider-runtime", error: { message } };
8491
+ const envelope = {
8492
+ sessionId: resolvedSessionId,
8493
+ provider: "flitro",
8494
+ seq: timeline.length + 1,
8495
+ epoch,
8496
+ timestamp: now(),
8497
+ item
8498
+ };
8499
+ appendEnvelope(envelope);
8500
+ }
8501
+ function getDriver() {
8502
+ if (runtimeDriver) return runtimeDriver;
8503
+ runtimeDriver = createFlitroDriver({
8504
+ client,
8505
+ sessionId: resolvedSessionId,
8506
+ model: options.model,
8507
+ skillNames: options.skillNames,
8508
+ mcpServerNames: options.mcpServerNames,
8509
+ approvalPolicy: options.approvalPolicy
8510
+ });
8511
+ return runtimeDriver;
8512
+ }
8513
+ async function sendToFlitro(message, sendOptions) {
8514
+ const driver = getDriver();
8515
+ try {
8516
+ await driver.sendMessage({ message, options: sendOptions }, {
8517
+ append: (item, rawRef) => {
8518
+ const envelope = {
8519
+ sessionId: resolvedSessionId,
8520
+ provider: "flitro",
8521
+ seq: timeline.length + 1,
8522
+ epoch,
8523
+ timestamp: now(),
8524
+ item,
8525
+ rawRef
8526
+ };
8527
+ appendEnvelope(envelope);
8528
+ return envelope;
8529
+ }
8530
+ });
8531
+ } catch (err) {
8532
+ const error = err instanceof Error ? err.message : String(err);
8533
+ dispatch({ type: "error", error });
8534
+ appendFailure(error);
8535
+ throw err;
8536
+ }
8537
+ }
8538
+ async function sendNextPendingMessage() {
8539
+ const next = pendingMessages.shift();
8540
+ if (!next) return;
8541
+ try {
8542
+ await sendToFlitro(next.message, next.options);
8543
+ } catch {
8544
+ }
8545
+ }
8546
+ async function ensureSession() {
8547
+ if (resolvedSessionId) return;
8548
+ const session = await client.createSession({
8549
+ model: options.model,
8550
+ skillNames: options.skillNames,
8551
+ mcpServerNames: options.mcpServerNames
8552
+ });
8553
+ resolvedSessionId = session.session_id;
8554
+ if (!options.epoch) {
8555
+ epoch = `flitro-${resolvedSessionId}`;
8556
+ }
8557
+ connectSse();
8558
+ }
8559
+ function connectSse() {
8560
+ if (!resolvedSessionId || sseStream) return;
8561
+ sseStream = createFlitroTimelineStream({
8562
+ url: client.sessionTimelineUrl(resolvedSessionId),
8563
+ apiKey: options.server.apiKey,
8564
+ tenantId: options.server.tenantId,
8565
+ // Scoped embed tokens rotate; always read the client's current bearer
8566
+ // and propagate refreshed tokens back to it.
8567
+ getBearerToken: () => client.getBearerToken(),
8568
+ onTokenExpired: options.server.onTokenExpired ? async () => {
8569
+ const fresh = await options.server.onTokenExpired?.();
8570
+ if (fresh) client.setToken(fresh);
8571
+ return fresh;
8572
+ } : void 0
8573
+ });
8574
+ sseStream.connect(
8575
+ // The client package types `item` as unknown; the server emits the
8576
+ // canonical Weft envelope, so this is the narrowing point.
8577
+ (envelope) => appendEnvelope(envelope),
8578
+ (_err) => {
8579
+ }
8580
+ );
8581
+ }
8582
+ if (resolvedSessionId) {
8583
+ connectSse();
8584
+ }
8585
+ return {
8586
+ get sessionId() {
8587
+ return resolvedSessionId;
8588
+ },
8589
+ provider: "flitro",
8590
+ runtimeKind: report.selected ?? "app-server",
8591
+ events: stream,
8592
+ commands: {
8593
+ async sendMessage(message, sendOptions) {
8594
+ await ensureSession();
8595
+ const shouldQueue = state.status === "running" || state.status === "waiting_for_permission";
8596
+ dispatch({ type: "send_message", message });
8597
+ if (shouldQueue) {
8598
+ pendingMessages.push({ message, options: sendOptions });
8599
+ return;
8600
+ }
8601
+ await sendToFlitro(message, sendOptions);
8602
+ },
8603
+ async abort(reason) {
8604
+ dispatch({ type: "abort", reason });
8605
+ pendingMessages.length = 0;
8606
+ await runtimeDriver?.abort?.(reason);
8607
+ },
8608
+ async respondToPermission(requestId, allowed, remember) {
8609
+ await getDriver().respondToPermission?.(requestId, allowed, remember);
8610
+ dispatch({ type: "permission_response" });
8611
+ },
8612
+ async resumeTool(runId, resumeData) {
8613
+ await getDriver().resumeTool?.(runId, resumeData);
8614
+ },
8615
+ async dispose() {
8616
+ dispatch({ type: "dispose" });
8617
+ pendingMessages.length = 0;
8618
+ sseStream?.disconnect();
8619
+ sseStream = void 0;
8620
+ await runtimeDriver?.dispose?.();
8621
+ }
8622
+ },
8623
+ async preflight() {
8624
+ dispatch({ type: "preflight_start" });
8625
+ if (!report.selected) {
8626
+ dispatch({ type: "preflight_error", error: report.error ?? "No runtime selected" });
8627
+ } else {
8628
+ dispatch({ type: "preflight_ok" });
8629
+ }
8630
+ const capItem = { type: "runtime_capability_report", report };
8631
+ const capEnvelope = {
8632
+ sessionId: resolvedSessionId,
8633
+ provider: "flitro",
8634
+ seq: timeline.length + 1,
8635
+ epoch,
8636
+ timestamp: now(),
8637
+ item: capItem
8638
+ };
8639
+ appendEnvelope(capEnvelope);
8640
+ return report;
8641
+ },
8642
+ async fetchTimeline(request = {}) {
8643
+ if (request.cursor?.epoch === epoch || !request.cursor) {
8644
+ const localResult = fetchTimeline(timeline, request);
8645
+ if (localResult.items.length > 0) return localResult;
8646
+ }
8647
+ if (resolvedSessionId) {
8648
+ try {
8649
+ const afterSeq = request.cursor?.afterSeq ?? 0;
8650
+ const result = await client.fetchTimeline(resolvedSessionId, afterSeq, request.limit);
8651
+ return {
8652
+ items: result.items,
8653
+ nextCursor: createTimelineCursor({
8654
+ epoch: result.nextCursor.epoch,
8655
+ afterSeq: result.nextCursor.afterSeq
8656
+ }),
8657
+ hasGap: result.hasGap
8658
+ };
8659
+ } catch {
8660
+ }
8661
+ }
8662
+ return {
8663
+ items: [],
8664
+ nextCursor: createTimelineCursor({ epoch, afterSeq: 0 }),
8665
+ hasGap: false
8666
+ };
8667
+ },
8668
+ getState() {
8669
+ return state;
8670
+ }
8671
+ };
8672
+ }
8673
+ function createFlitroEmbedRuntime(options) {
8674
+ return createFlitroProviderRuntime({
8675
+ server: {
8676
+ baseUrl: options.baseUrl,
8677
+ token: options.token,
8678
+ onTokenExpired: options.onTokenExpired
8679
+ },
8680
+ sessionId: options.sessionId,
8681
+ epoch: options.epoch,
8682
+ now: options.now,
8683
+ extensions: options.extensions,
8684
+ candidates: [{ kind: "app-server", available: true }],
8685
+ auth: { mode: "provider-owned", configured: true, source: "flitro-embed" }
8686
+ });
8687
+ }
8688
+ var PushTimelineStream = class {
8689
+ listeners = /* @__PURE__ */ new Set();
8690
+ connect(onEvent, onError, onClose) {
8691
+ this.listeners.add({ onEvent, onError, onClose });
8692
+ }
8693
+ disconnect() {
8694
+ for (const l of this.listeners) l.onClose?.();
8695
+ this.listeners.clear();
8696
+ }
8697
+ isConnected() {
8698
+ return this.listeners.size > 0;
8699
+ }
8700
+ emit(event) {
8701
+ for (const l of this.listeners) l.onEvent(event);
8702
+ }
8703
+ };
8704
+
8705
+ // ../packages/react/src/use-agent-session.ts
8706
+ function useAgentSession(options) {
8707
+ const onTokenExpiredRef = useRef9(options.onTokenExpired);
8708
+ onTokenExpiredRef.current = options.onTokenExpired;
8709
+ const tokenRef = useRef9(options.token);
8710
+ tokenRef.current = options.token;
8711
+ const runtime = useMemo9(() => {
8712
+ return createDeferredAgentRuntime({
8713
+ provider: "flitro",
8714
+ runtimeKind: "app-server",
8715
+ sessionId: options.sessionId,
8716
+ createRuntime: () => createFlitroEmbedRuntime({
8717
+ baseUrl: options.server,
8718
+ token: tokenRef.current,
8719
+ sessionId: options.sessionId,
8720
+ onTokenExpired: () => onTokenExpiredRef.current?.()
8721
+ })
8722
+ });
8723
+ }, [options.server, options.sessionId]);
8724
+ useEffect10(() => {
8725
+ return () => {
8726
+ void runtime.disposeIfCreated();
8727
+ };
8728
+ }, [runtime]);
8729
+ return { runtime, sessionId: options.sessionId, server: options.server };
8730
+ }
8731
+ function createDeferredAgentRuntime(options) {
8732
+ let runtime = null;
8733
+ let disposed = false;
8734
+ const getRuntime = () => {
8735
+ if (disposed) {
8736
+ throw new Error("Agent runtime has been disposed");
8737
+ }
8738
+ runtime ??= options.createRuntime();
8739
+ return runtime;
8740
+ };
8741
+ return {
8742
+ get sessionId() {
8743
+ return runtime?.sessionId ?? options.sessionId;
8744
+ },
8745
+ get provider() {
8746
+ return runtime?.provider ?? options.provider;
8747
+ },
8748
+ get runtimeKind() {
8749
+ return runtime?.runtimeKind ?? options.runtimeKind;
8750
+ },
8751
+ events: {
8752
+ connect(onEvent, onError, onClose) {
8753
+ getRuntime().events.connect(onEvent, onError, onClose);
8754
+ },
8755
+ disconnect() {
8756
+ runtime?.events.disconnect();
8757
+ },
8758
+ isConnected() {
8759
+ return runtime?.events.isConnected() ?? false;
8760
+ }
8761
+ },
8762
+ commands: {
8763
+ sendMessage(message, sendOptions) {
8764
+ return getRuntime().commands.sendMessage(message, sendOptions);
8765
+ },
8766
+ abort(reason) {
8767
+ return getRuntime().commands.abort(reason);
8768
+ },
8769
+ respondToPermission(requestId, allowed, remember) {
8770
+ return getRuntime().commands.respondToPermission(requestId, allowed, remember);
8771
+ },
8772
+ async resumeTool(runId, resumeData) {
8773
+ const commands = getRuntime().commands;
8774
+ if (!commands.resumeTool) {
8775
+ throw new Error(`resumeTool is not supported by the ${options.provider} runtime`);
8776
+ }
8777
+ await commands.resumeTool(runId, resumeData);
8778
+ },
8779
+ dispose() {
8780
+ disposed = true;
8781
+ const current = runtime;
8782
+ runtime = null;
8783
+ return current?.commands.dispose() ?? Promise.resolve();
8784
+ }
8785
+ },
8786
+ preflight() {
8787
+ return getRuntime().preflight();
8788
+ },
8789
+ fetchTimeline(request) {
8790
+ return getRuntime().fetchTimeline(request);
8791
+ },
8792
+ getState() {
8793
+ return runtime?.getState() ?? {
8794
+ status: initialRuntimeState.status,
8795
+ acceptedMessages: [],
8796
+ queuedMessages: []
8797
+ };
8798
+ },
8799
+ async disposeIfCreated() {
8800
+ if (!runtime) return;
8801
+ const current = runtime;
8802
+ runtime = null;
8803
+ await current.commands.dispose();
8804
+ }
8805
+ };
8806
+ }
7828
8807
  export {
7829
8808
  ActivityDetailsPanel,
7830
8809
  ActivityInspector,