@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.cjs CHANGED
@@ -88,7 +88,7 @@ __export(chat_exports, {
88
88
  toStoredSession: () => toStoredSession,
89
89
  updateMessageAt: () => updateMessageAt,
90
90
  useAgentChatSession: () => useAgentChatSession,
91
- useAgentSession: () => import_weft_react.useAgentSession,
91
+ useAgentSession: () => useAgentSession,
92
92
  useEventProcessor: () => useEventProcessor,
93
93
  useTimelineAgentChatSession: () => useTimelineAgentChatSession
94
94
  });
@@ -7119,6 +7119,27 @@ var import_react18 = require("react");
7119
7119
  var import_react19 = require("react");
7120
7120
 
7121
7121
  // ../packages/timeline/dist/index.js
7122
+ function createTimelineCursor(cursor) {
7123
+ return {
7124
+ epoch: cursor.epoch,
7125
+ afterSeq: cursor.afterSeq
7126
+ };
7127
+ }
7128
+ function fetchTimeline(timeline, request = {}) {
7129
+ const ordered = sortTimeline(timeline);
7130
+ const cursor = request.cursor ?? cursorFromTimelineStart(ordered);
7131
+ const items = ordered.filter((item) => item.epoch === cursor.epoch && item.seq > cursor.afterSeq).slice(0, request.limit);
7132
+ const lastSeq = items.at(-1)?.seq ?? cursor.afterSeq;
7133
+ const firstSeq = items[0]?.seq;
7134
+ return {
7135
+ items,
7136
+ nextCursor: {
7137
+ epoch: cursor.epoch,
7138
+ afterSeq: lastSeq
7139
+ },
7140
+ hasGap: firstSeq !== void 0 && firstSeq > cursor.afterSeq + 1
7141
+ };
7142
+ }
7122
7143
  function mergeTimeline(existing, incoming) {
7123
7144
  const byKey = /* @__PURE__ */ new Map();
7124
7145
  for (const item of [...existing, ...incoming]) {
@@ -7126,6 +7147,12 @@ function mergeTimeline(existing, incoming) {
7126
7147
  }
7127
7148
  return sortTimeline([...byKey.values()]);
7128
7149
  }
7150
+ function cursorFromTimelineStart(timeline) {
7151
+ return {
7152
+ epoch: timeline[0]?.epoch ?? "default",
7153
+ afterSeq: 0
7154
+ };
7155
+ }
7129
7156
  function sortTimeline(timeline) {
7130
7157
  return [...timeline].sort((left, right) => {
7131
7158
  if (left.epoch !== right.epoch) return left.epoch.localeCompare(right.epoch);
@@ -7899,8 +7926,960 @@ function TimelineAgentChatPanel({
7899
7926
  ] });
7900
7927
  }
7901
7928
 
7902
- // src/chat.ts
7903
- var import_weft_react = require("@percena/weft-react");
7929
+ // ../packages/react/src/use-agent-session.ts
7930
+ var import_react21 = require("react");
7931
+
7932
+ // ../packages/runtime-core/dist/index.js
7933
+ var RUNTIME_KINDS = ["native-sdk", "app-server", "compatible-sdk", "cli-fallback"];
7934
+ var initialRuntimeState = {
7935
+ status: "idle",
7936
+ acceptedMessages: [],
7937
+ queuedMessages: []
7938
+ };
7939
+ function reduceRuntimeState(state = initialRuntimeState, action) {
7940
+ switch (action.type) {
7941
+ case "preflight_start":
7942
+ return { ...state, status: "preflighting", lastError: void 0 };
7943
+ case "preflight_ok":
7944
+ return { ...state, status: "ready", lastError: void 0 };
7945
+ case "preflight_error":
7946
+ return { ...state, status: "failed", lastError: action.error };
7947
+ case "starting":
7948
+ if (state.status === "ready" || state.status === "idle") {
7949
+ return { ...state, status: "starting" };
7950
+ }
7951
+ return state;
7952
+ case "send_message":
7953
+ if (state.status === "running" || state.status === "waiting_for_permission") {
7954
+ return {
7955
+ ...state,
7956
+ queuedMessages: [...state.queuedMessages, action.message]
7957
+ };
7958
+ }
7959
+ return {
7960
+ ...state,
7961
+ status: "running",
7962
+ acceptedMessages: [...state.acceptedMessages, action.message]
7963
+ };
7964
+ case "permission_request":
7965
+ return {
7966
+ ...state,
7967
+ status: "waiting_for_permission",
7968
+ waitingPermissionRequestId: action.requestId
7969
+ };
7970
+ case "permission_response":
7971
+ return {
7972
+ ...state,
7973
+ status: "running",
7974
+ waitingPermissionRequestId: void 0
7975
+ };
7976
+ case "turn_completed":
7977
+ if (state.status === "running") {
7978
+ return { ...state, status: "turn_completed" };
7979
+ }
7980
+ return state;
7981
+ case "complete": {
7982
+ if (state.status === "turn_completed") {
7983
+ const [nextMessage2, ...remaining2] = state.queuedMessages;
7984
+ if (nextMessage2) {
7985
+ return {
7986
+ ...state,
7987
+ status: "running",
7988
+ acceptedMessages: [...state.acceptedMessages, nextMessage2],
7989
+ queuedMessages: remaining2
7990
+ };
7991
+ }
7992
+ return { ...state, status: "ready" };
7993
+ }
7994
+ const [nextMessage, ...remaining] = state.queuedMessages;
7995
+ if (nextMessage) {
7996
+ return {
7997
+ ...state,
7998
+ status: "running",
7999
+ acceptedMessages: [...state.acceptedMessages, nextMessage],
8000
+ queuedMessages: remaining
8001
+ };
8002
+ }
8003
+ return { ...state, status: "ready" };
8004
+ }
8005
+ case "abort":
8006
+ return {
8007
+ ...state,
8008
+ status: "ready",
8009
+ lastError: action.reason,
8010
+ queuedMessages: [],
8011
+ waitingPermissionRequestId: void 0
8012
+ };
8013
+ case "error":
8014
+ return { ...state, status: "failed", lastError: action.error };
8015
+ case "dispose":
8016
+ return {
8017
+ ...state,
8018
+ status: "disposed",
8019
+ queuedMessages: [],
8020
+ waitingPermissionRequestId: void 0
8021
+ };
8022
+ }
8023
+ }
8024
+ function createRuntimeExtensionContext(context = {}) {
8025
+ return {
8026
+ policy: context.policy,
8027
+ sources: context.sources ? { enabledSourceSlugs: unique(context.sources.enabledSourceSlugs) } : void 0,
8028
+ skills: context.skills ? { activeSkillSlugs: unique(context.skills.activeSkillSlugs) } : void 0,
8029
+ commandOrigin: context.commandOrigin,
8030
+ hostServices: context.hostServices
8031
+ };
8032
+ }
8033
+ function unique(values) {
8034
+ return [...new Set(values)];
8035
+ }
8036
+ function findCandidate(candidates, kind) {
8037
+ return candidates.find((candidate) => candidate.kind === kind);
8038
+ }
8039
+ function defaultRuntimeKindOrder(provider) {
8040
+ if (provider === "codex") {
8041
+ return ["app-server", "native-sdk", "compatible-sdk", "cli-fallback"];
8042
+ }
8043
+ return RUNTIME_KINDS;
8044
+ }
8045
+ function firstAvailableCandidate(candidates, provider) {
8046
+ for (const kind of defaultRuntimeKindOrder(provider)) {
8047
+ const candidate = findCandidate(candidates, kind);
8048
+ if (candidate?.available) return candidate;
8049
+ }
8050
+ return void 0;
8051
+ }
8052
+ function selectRuntimeCandidate(options) {
8053
+ const preferred = options.preferredRuntime ? findCandidate(options.candidates, options.preferredRuntime) : void 0;
8054
+ if (preferred?.available) {
8055
+ return { selected: preferred.kind, fallback: false };
8056
+ }
8057
+ if (preferred && options.allowFallback !== true) {
8058
+ return {
8059
+ fallback: false,
8060
+ error: preferred.reason ?? `${preferred.kind} runtime is unavailable`
8061
+ };
8062
+ }
8063
+ const selected = firstAvailableCandidate(options.candidates, options.provider);
8064
+ if (!selected) {
8065
+ return {
8066
+ fallback: false,
8067
+ error: options.candidates.map((candidate) => candidate.reason).filter(Boolean).join("; ") || "No runtime candidates are available"
8068
+ };
8069
+ }
8070
+ const fallback = Boolean(preferred && selected.kind !== preferred.kind);
8071
+ return {
8072
+ selected: selected.kind,
8073
+ fallback,
8074
+ fallbackReason: fallback ? preferred?.reason ?? `${preferred?.kind ?? "preferred"} runtime is unavailable` : void 0
8075
+ };
8076
+ }
8077
+ function createRuntimeCapabilityReport(options) {
8078
+ const selection = selectRuntimeCandidate(options);
8079
+ const extensionCapabilities = normalizeExtensionCapabilities(options.extensionCapabilities);
8080
+ return {
8081
+ provider: options.provider,
8082
+ candidates: options.candidates,
8083
+ preferredRuntime: options.preferredRuntime,
8084
+ allowFallback: options.allowFallback === true,
8085
+ auth: options.auth,
8086
+ ...extensionCapabilities,
8087
+ ...selection
8088
+ };
8089
+ }
8090
+ function normalizeExtensionCapabilities(capabilities = {}) {
8091
+ return {
8092
+ policyCapabilities: capabilities.policy ?? {
8093
+ supported: false,
8094
+ modes: [],
8095
+ approvals: false,
8096
+ toolPolicy: false
8097
+ },
8098
+ sourceCapabilities: capabilities.sources ?? {
8099
+ supported: false
8100
+ },
8101
+ skillCapabilities: capabilities.skills ?? {
8102
+ supported: false
8103
+ },
8104
+ automationCapabilities: capabilities.automations ?? {
8105
+ supported: false,
8106
+ eventBus: false,
8107
+ schedulerHost: false,
8108
+ promptAction: false,
8109
+ webhookAction: false
8110
+ },
8111
+ hostToolCapabilities: capabilities.hostTools ?? {
8112
+ supported: false,
8113
+ sessionTools: false,
8114
+ workflowTransitions: false,
8115
+ browserActions: false,
8116
+ metadataWrites: false
8117
+ }
8118
+ };
8119
+ }
8120
+
8121
+ // ../packages/client/dist/index.js
8122
+ var FlitroHttpClient = class {
8123
+ baseUrl;
8124
+ timeout;
8125
+ apiKey;
8126
+ tenantId;
8127
+ onTokenExpired;
8128
+ token;
8129
+ constructor(options) {
8130
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
8131
+ this.timeout = options.timeout ?? 3e4;
8132
+ this.apiKey = options.apiKey ?? "";
8133
+ this.tenantId = options.tenantId ?? "";
8134
+ this.token = options.token ?? "";
8135
+ this.onTokenExpired = options.onTokenExpired;
8136
+ }
8137
+ /** Current Authorization bearer (scoped token wins over apiKey). */
8138
+ getBearerToken() {
8139
+ return this.token || this.apiKey;
8140
+ }
8141
+ /** Replace the scoped token (e.g. after a host-backend refresh). */
8142
+ setToken(token) {
8143
+ this.token = token;
8144
+ }
8145
+ buildHeaders() {
8146
+ const headers = { "Content-Type": "application/json" };
8147
+ const bearer = this.getBearerToken();
8148
+ if (bearer) headers["Authorization"] = `Bearer ${bearer}`;
8149
+ if (!this.token && this.tenantId) headers["X-Tenant-ID"] = this.tenantId;
8150
+ return headers;
8151
+ }
8152
+ async preflight() {
8153
+ return this.get("/v1/capabilities");
8154
+ }
8155
+ async health() {
8156
+ return this.get("/v1/health");
8157
+ }
8158
+ async createSession(options) {
8159
+ return this.post("/v1/sessions", {
8160
+ title: options?.title ?? "Weft session",
8161
+ model: options?.model,
8162
+ skill_names: options?.skillNames,
8163
+ mcp_server_names: options?.mcpServerNames
8164
+ });
8165
+ }
8166
+ async getSession(sessionId) {
8167
+ return this.get(`/v1/sessions/${encodeURIComponent(sessionId)}`);
8168
+ }
8169
+ async sendMessage(sessionId, message, options) {
8170
+ const budget = options?.budget ? {
8171
+ max_steps: options.budget.maxSteps,
8172
+ max_tokens: options.budget.maxTokens,
8173
+ max_wall_time_sec: options.budget.maxWallTimeSec
8174
+ } : void 0;
8175
+ return this.post(`/v1/sessions/${encodeURIComponent(sessionId)}/runs`, {
8176
+ message,
8177
+ model: options?.model,
8178
+ skill_names: options?.skillNames,
8179
+ mcp_server_names: options?.mcpServerNames,
8180
+ tool_names: options?.toolNames,
8181
+ approval_policy: options?.approvalPolicy,
8182
+ execution_mode: options?.executionMode,
8183
+ workspace_id: options?.workspaceId,
8184
+ budget
8185
+ });
8186
+ }
8187
+ async cancelRun(runId) {
8188
+ return this.post(`/v1/runs/${encodeURIComponent(runId)}/cancel`, {});
8189
+ }
8190
+ async resumeTool(runId, resumeData) {
8191
+ return this.post(
8192
+ `/v1/runs/${encodeURIComponent(runId)}/resume-tool`,
8193
+ { resume_data: resumeData }
8194
+ );
8195
+ }
8196
+ async respondToPermission(sessionId, requestId, allowed, options) {
8197
+ return this.post(
8198
+ `/v1/sessions/${encodeURIComponent(sessionId)}/permission-response`,
8199
+ { requestId, allowed, remember: options?.remember, text: options?.text, answer: options?.answer }
8200
+ );
8201
+ }
8202
+ async patchSession(sessionId, patch) {
8203
+ return this.patch(
8204
+ `/v1/sessions/${encodeURIComponent(sessionId)}`,
8205
+ patch
8206
+ );
8207
+ }
8208
+ async listModels() {
8209
+ return this.get("/v1/models");
8210
+ }
8211
+ async fetchTimeline(sessionId, afterSeq, limit) {
8212
+ const params = new URLSearchParams();
8213
+ if (afterSeq !== void 0) params.set("after_seq", String(afterSeq));
8214
+ if (limit !== void 0) params.set("limit", String(limit));
8215
+ const qs = params.toString();
8216
+ const url = `/v1/sessions/${encodeURIComponent(sessionId)}/timeline/fetch${qs ? `?${qs}` : ""}`;
8217
+ return this.get(url);
8218
+ }
8219
+ /** Returns the SSE stream URL for a session's canonical timeline. */
8220
+ sessionTimelineUrl(sessionId) {
8221
+ return `${this.baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/timeline`;
8222
+ }
8223
+ async get(path) {
8224
+ return this.request("GET", path, void 0);
8225
+ }
8226
+ async post(path, body) {
8227
+ return this.request("POST", path, body);
8228
+ }
8229
+ async patch(path, body) {
8230
+ return this.request("PATCH", path, body);
8231
+ }
8232
+ async request(method, path, body, isRetry = false) {
8233
+ const url = `${this.baseUrl}${path}`;
8234
+ const controller = new AbortController();
8235
+ const timer = setTimeout(() => controller.abort(), this.timeout);
8236
+ try {
8237
+ const response = await fetch(url, {
8238
+ method,
8239
+ headers: this.buildHeaders(),
8240
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
8241
+ signal: controller.signal
8242
+ });
8243
+ if (response.status === 401 && this.token && this.onTokenExpired && !isRetry) {
8244
+ const fresh = await this.onTokenExpired();
8245
+ if (fresh) {
8246
+ this.token = fresh;
8247
+ return this.request(method, path, body, true);
8248
+ }
8249
+ }
8250
+ if (!response.ok) {
8251
+ let errorMsg = `Flitro HTTP ${response.status}`;
8252
+ try {
8253
+ const errBody = await response.json();
8254
+ if (errBody.error) errorMsg = `${errorMsg}: ${errBody.error}`;
8255
+ } catch {
8256
+ }
8257
+ throw new Error(errorMsg);
8258
+ }
8259
+ return response.json();
8260
+ } finally {
8261
+ clearTimeout(timer);
8262
+ }
8263
+ }
8264
+ };
8265
+ var FlitroFetchSseTimelineStream = class {
8266
+ listeners = /* @__PURE__ */ new Set();
8267
+ abortController = null;
8268
+ connected = false;
8269
+ disposed = false;
8270
+ afterSeq;
8271
+ reconnectAttempts = 0;
8272
+ tokenOverride = "";
8273
+ getBearerToken;
8274
+ onTokenExpired;
8275
+ options;
8276
+ constructor(options) {
8277
+ this.afterSeq = options.initialAfterSeq ?? 0;
8278
+ this.getBearerToken = options.getBearerToken;
8279
+ this.onTokenExpired = options.onTokenExpired;
8280
+ this.options = {
8281
+ url: options.url,
8282
+ apiKey: options.apiKey ?? "",
8283
+ tenantId: options.tenantId ?? "",
8284
+ reconnectDelayMs: options.reconnectDelayMs ?? 1500,
8285
+ maxReconnectAttempts: options.maxReconnectAttempts ?? 20,
8286
+ initialAfterSeq: this.afterSeq,
8287
+ now: options.now ?? (() => Date.now())
8288
+ };
8289
+ }
8290
+ connect(onEvent, onError, onClose) {
8291
+ this.disposed = false;
8292
+ this.listeners.add({ onEvent, onError, onClose });
8293
+ if (!this.connected) {
8294
+ this.connected = true;
8295
+ this.reconnectAttempts = 0;
8296
+ this.startFetch();
8297
+ }
8298
+ }
8299
+ disconnect() {
8300
+ this.disposed = true;
8301
+ this.connected = false;
8302
+ this.abortController?.abort();
8303
+ for (const listener of this.listeners) {
8304
+ listener.onClose?.();
8305
+ }
8306
+ this.listeners.clear();
8307
+ }
8308
+ isConnected() {
8309
+ return this.connected && !this.disposed;
8310
+ }
8311
+ buildUrl() {
8312
+ let url = this.options.url;
8313
+ if (this.afterSeq > 0) {
8314
+ url += (url.includes("?") ? "&" : "?") + `after_seq=${this.afterSeq}`;
8315
+ }
8316
+ return url;
8317
+ }
8318
+ buildHeaders() {
8319
+ const h = { Accept: "text/event-stream" };
8320
+ const bearer = this.tokenOverride || this.getBearerToken?.() || this.options.apiKey;
8321
+ if (bearer) h["Authorization"] = `Bearer ${bearer}`;
8322
+ if (this.options.tenantId) h["X-Tenant-ID"] = this.options.tenantId;
8323
+ return h;
8324
+ }
8325
+ async startFetch() {
8326
+ if (this.disposed) return;
8327
+ this.abortController = new AbortController();
8328
+ try {
8329
+ const response = await fetch(this.buildUrl(), {
8330
+ headers: this.buildHeaders(),
8331
+ signal: this.abortController.signal
8332
+ });
8333
+ if (response.status === 401 && this.onTokenExpired) {
8334
+ const fresh = await this.onTokenExpired();
8335
+ if (fresh) this.tokenOverride = fresh;
8336
+ }
8337
+ if (!response.ok || !response.body) {
8338
+ throw new Error(`Flitro SSE: HTTP ${response.status}`);
8339
+ }
8340
+ this.reconnectAttempts = 0;
8341
+ const reader = response.body.getReader();
8342
+ const decoder = new TextDecoder();
8343
+ let buffer = "";
8344
+ while (true) {
8345
+ const { done, value } = await reader.read();
8346
+ if (done) break;
8347
+ buffer += decoder.decode(value, { stream: true });
8348
+ const envelopes = parseSseBuffer(buffer);
8349
+ buffer = envelopes.remainder;
8350
+ for (const envelope of envelopes.items) {
8351
+ if (envelope.seq > this.afterSeq) {
8352
+ this.afterSeq = envelope.seq;
8353
+ }
8354
+ for (const listener of this.listeners) {
8355
+ listener.onEvent(envelope);
8356
+ }
8357
+ }
8358
+ }
8359
+ if (!this.disposed) {
8360
+ this.connected = false;
8361
+ this.scheduleReconnect();
8362
+ }
8363
+ } catch (err) {
8364
+ if (this.disposed) return;
8365
+ if (err instanceof Error && err.name === "AbortError") return;
8366
+ this.connected = false;
8367
+ this.scheduleReconnect();
8368
+ }
8369
+ }
8370
+ scheduleReconnect() {
8371
+ if (this.disposed) return;
8372
+ if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
8373
+ this.emitError(new Error(`Flitro SSE: max reconnect attempts reached`));
8374
+ return;
8375
+ }
8376
+ this.reconnectAttempts++;
8377
+ const delay = Math.min(this.options.reconnectDelayMs * this.reconnectAttempts, 3e4);
8378
+ setTimeout(() => {
8379
+ this.connected = true;
8380
+ this.startFetch();
8381
+ }, delay);
8382
+ }
8383
+ emitError(err) {
8384
+ for (const listener of this.listeners) listener.onError?.(err);
8385
+ }
8386
+ };
8387
+ function parseSseBuffer(buffer) {
8388
+ const items = [];
8389
+ const blocks = buffer.split("\n\n");
8390
+ const remainder = blocks.pop() ?? "";
8391
+ for (const block of blocks) {
8392
+ let eventType = "";
8393
+ let data = "";
8394
+ for (const line of block.split("\n")) {
8395
+ if (line.startsWith("event: ")) {
8396
+ eventType = line.slice(7).trim();
8397
+ } else if (line.startsWith("data: ")) {
8398
+ data = line.slice(6).trim();
8399
+ }
8400
+ }
8401
+ if (eventType === "timeline" && data) {
8402
+ try {
8403
+ items.push(JSON.parse(data));
8404
+ } catch {
8405
+ }
8406
+ }
8407
+ }
8408
+ return { items, remainder };
8409
+ }
8410
+ function createFlitroTimelineStream(options) {
8411
+ return new FlitroFetchSseTimelineStream(options);
8412
+ }
8413
+
8414
+ // ../packages/provider-flitro/dist/index.js
8415
+ function mapPermissionModeToApprovalPolicy(mode) {
8416
+ if (mode === "allow-all") return "auto";
8417
+ if (mode === "safe") return "never";
8418
+ if (mode === "ask") return "on-request";
8419
+ return void 0;
8420
+ }
8421
+ function createFlitroDriver(options) {
8422
+ let activeRunId;
8423
+ return {
8424
+ async sendMessage(input, _sequencer) {
8425
+ const run = await options.client.sendMessage(
8426
+ options.sessionId,
8427
+ input.message,
8428
+ {
8429
+ model: input.options?.model ?? options.model,
8430
+ skillNames: options.skillNames,
8431
+ mcpServerNames: options.mcpServerNames,
8432
+ // Per-message permission choice (from the chat panel selector) wins
8433
+ // over the runtime's creation-time default.
8434
+ approvalPolicy: mapPermissionModeToApprovalPolicy(input.options?.permissionMode) ?? options.approvalPolicy
8435
+ }
8436
+ );
8437
+ activeRunId = run.run_id;
8438
+ },
8439
+ async abort(reason) {
8440
+ if (activeRunId) {
8441
+ await options.client.cancelRun(activeRunId).catch(() => {
8442
+ });
8443
+ activeRunId = void 0;
8444
+ }
8445
+ },
8446
+ async respondToPermission(requestId, allowed, remember) {
8447
+ await options.client.respondToPermission(options.sessionId, requestId, allowed, {
8448
+ remember
8449
+ });
8450
+ },
8451
+ async resumeTool(runId, resumeData) {
8452
+ await options.client.resumeTool(runId, resumeData);
8453
+ },
8454
+ async dispose() {
8455
+ if (activeRunId) {
8456
+ await options.client.cancelRun(activeRunId).catch(() => {
8457
+ });
8458
+ activeRunId = void 0;
8459
+ }
8460
+ }
8461
+ };
8462
+ }
8463
+ function createFlitroRuntimeCapabilityReport(options) {
8464
+ const appServerAvailable = options.candidates.some((c) => c.kind === "app-server" && c.available);
8465
+ return createRuntimeCapabilityReport({
8466
+ provider: "flitro",
8467
+ candidates: options.candidates,
8468
+ preferredRuntime: "app-server",
8469
+ allowFallback: options.allowFallback,
8470
+ auth: options.auth,
8471
+ extensionCapabilities: {
8472
+ policy: {
8473
+ supported: true,
8474
+ degraded: !appServerAvailable || void 0,
8475
+ modes: ["safe", "ask", "allow-all"],
8476
+ approvals: appServerAvailable,
8477
+ toolPolicy: appServerAvailable,
8478
+ reason: appServerAvailable ? void 0 : "Flitro server is unavailable"
8479
+ },
8480
+ sources: {
8481
+ supported: appServerAvailable,
8482
+ registry: appServerAvailable,
8483
+ mcpTools: appServerAvailable,
8484
+ credentialGateway: false,
8485
+ reason: appServerAvailable ? void 0 : "Source tools require a running Flitro server"
8486
+ },
8487
+ skills: {
8488
+ supported: appServerAvailable,
8489
+ registry: appServerAvailable,
8490
+ activationPlan: appServerAvailable,
8491
+ scopedPolicy: appServerAvailable,
8492
+ reason: appServerAvailable ? void 0 : "Skill activation requires a running Flitro server"
8493
+ },
8494
+ automations: {
8495
+ supported: appServerAvailable,
8496
+ degraded: !appServerAvailable || void 0,
8497
+ eventBus: appServerAvailable,
8498
+ schedulerHost: appServerAvailable,
8499
+ promptAction: appServerAvailable,
8500
+ webhookAction: false,
8501
+ reason: appServerAvailable ? void 0 : "Automations require a running Flitro server"
8502
+ },
8503
+ hostTools: {
8504
+ supported: appServerAvailable,
8505
+ sessionTools: false,
8506
+ workflowTransitions: false,
8507
+ browserActions: false,
8508
+ metadataWrites: appServerAvailable
8509
+ }
8510
+ }
8511
+ });
8512
+ }
8513
+ function createFlitroProviderRuntime(options) {
8514
+ const report = createFlitroRuntimeCapabilityReport(options);
8515
+ const extensions = createRuntimeExtensionContext(options.extensions);
8516
+ const client = new FlitroHttpClient(options.server);
8517
+ let resolvedSessionId = options.sessionId ?? "";
8518
+ let epoch = options.epoch ?? (resolvedSessionId ? `flitro-${resolvedSessionId}` : "flitro-pending");
8519
+ const now = options.now ?? (() => Date.now());
8520
+ const timeline = [];
8521
+ const seenTimelineKeys = /* @__PURE__ */ new Set();
8522
+ const stream = new PushTimelineStream();
8523
+ let state = initialRuntimeState;
8524
+ let runtimeDriver = options.driver;
8525
+ let sseStream;
8526
+ const pendingMessages = [];
8527
+ function dispatch(action) {
8528
+ state = reduceRuntimeState(state, action);
8529
+ }
8530
+ function syncStateFromEnvelope(envelope) {
8531
+ const item = envelope.item;
8532
+ if (item.type === "permission_requested") {
8533
+ dispatch({ type: "permission_request", requestId: item.request.requestId });
8534
+ } else if (item.type === "permission_resolved") {
8535
+ dispatch({ type: "permission_response" });
8536
+ } else if (item.type === "turn_completed") {
8537
+ dispatch({ type: "turn_completed" });
8538
+ dispatch({ type: "complete" });
8539
+ void sendNextPendingMessage();
8540
+ } else if (item.type === "turn_failed") {
8541
+ const err = item.error;
8542
+ dispatch({ type: "error", error: err?.message ?? "turn failed" });
8543
+ } else if (item.type === "tool_suspended") {
8544
+ dispatch({ type: "permission_request", requestId: `tool-suspend:${item.callId}` });
8545
+ } else if (item.type === "tool_resumed") {
8546
+ dispatch({ type: "permission_response" });
8547
+ } else if (item.type === "session_status") {
8548
+ if (item.status === "running") {
8549
+ dispatch({ type: "starting" });
8550
+ } else if (item.status === "ended") {
8551
+ dispatch({ type: "abort" });
8552
+ }
8553
+ }
8554
+ }
8555
+ function appendEnvelope(envelope) {
8556
+ if (envelope.seq > 0) {
8557
+ const key = `${envelope.epoch || envelope.sessionId || "unknown"}:${envelope.seq}`;
8558
+ if (seenTimelineKeys.has(key)) return;
8559
+ seenTimelineKeys.add(key);
8560
+ }
8561
+ syncStateFromEnvelope(envelope);
8562
+ timeline.push(envelope);
8563
+ stream.emit(envelope);
8564
+ }
8565
+ function appendFailure(message) {
8566
+ const item = { type: "turn_failed", turnId: "provider-runtime", error: { message } };
8567
+ const envelope = {
8568
+ sessionId: resolvedSessionId,
8569
+ provider: "flitro",
8570
+ seq: timeline.length + 1,
8571
+ epoch,
8572
+ timestamp: now(),
8573
+ item
8574
+ };
8575
+ appendEnvelope(envelope);
8576
+ }
8577
+ function getDriver() {
8578
+ if (runtimeDriver) return runtimeDriver;
8579
+ runtimeDriver = createFlitroDriver({
8580
+ client,
8581
+ sessionId: resolvedSessionId,
8582
+ model: options.model,
8583
+ skillNames: options.skillNames,
8584
+ mcpServerNames: options.mcpServerNames,
8585
+ approvalPolicy: options.approvalPolicy
8586
+ });
8587
+ return runtimeDriver;
8588
+ }
8589
+ async function sendToFlitro(message, sendOptions) {
8590
+ const driver = getDriver();
8591
+ try {
8592
+ await driver.sendMessage({ message, options: sendOptions }, {
8593
+ append: (item, rawRef) => {
8594
+ const envelope = {
8595
+ sessionId: resolvedSessionId,
8596
+ provider: "flitro",
8597
+ seq: timeline.length + 1,
8598
+ epoch,
8599
+ timestamp: now(),
8600
+ item,
8601
+ rawRef
8602
+ };
8603
+ appendEnvelope(envelope);
8604
+ return envelope;
8605
+ }
8606
+ });
8607
+ } catch (err) {
8608
+ const error = err instanceof Error ? err.message : String(err);
8609
+ dispatch({ type: "error", error });
8610
+ appendFailure(error);
8611
+ throw err;
8612
+ }
8613
+ }
8614
+ async function sendNextPendingMessage() {
8615
+ const next = pendingMessages.shift();
8616
+ if (!next) return;
8617
+ try {
8618
+ await sendToFlitro(next.message, next.options);
8619
+ } catch {
8620
+ }
8621
+ }
8622
+ async function ensureSession() {
8623
+ if (resolvedSessionId) return;
8624
+ const session = await client.createSession({
8625
+ model: options.model,
8626
+ skillNames: options.skillNames,
8627
+ mcpServerNames: options.mcpServerNames
8628
+ });
8629
+ resolvedSessionId = session.session_id;
8630
+ if (!options.epoch) {
8631
+ epoch = `flitro-${resolvedSessionId}`;
8632
+ }
8633
+ connectSse();
8634
+ }
8635
+ function connectSse() {
8636
+ if (!resolvedSessionId || sseStream) return;
8637
+ sseStream = createFlitroTimelineStream({
8638
+ url: client.sessionTimelineUrl(resolvedSessionId),
8639
+ apiKey: options.server.apiKey,
8640
+ tenantId: options.server.tenantId,
8641
+ // Scoped embed tokens rotate; always read the client's current bearer
8642
+ // and propagate refreshed tokens back to it.
8643
+ getBearerToken: () => client.getBearerToken(),
8644
+ onTokenExpired: options.server.onTokenExpired ? async () => {
8645
+ const fresh = await options.server.onTokenExpired?.();
8646
+ if (fresh) client.setToken(fresh);
8647
+ return fresh;
8648
+ } : void 0
8649
+ });
8650
+ sseStream.connect(
8651
+ // The client package types `item` as unknown; the server emits the
8652
+ // canonical Weft envelope, so this is the narrowing point.
8653
+ (envelope) => appendEnvelope(envelope),
8654
+ (_err) => {
8655
+ }
8656
+ );
8657
+ }
8658
+ if (resolvedSessionId) {
8659
+ connectSse();
8660
+ }
8661
+ return {
8662
+ get sessionId() {
8663
+ return resolvedSessionId;
8664
+ },
8665
+ provider: "flitro",
8666
+ runtimeKind: report.selected ?? "app-server",
8667
+ events: stream,
8668
+ commands: {
8669
+ async sendMessage(message, sendOptions) {
8670
+ await ensureSession();
8671
+ const shouldQueue = state.status === "running" || state.status === "waiting_for_permission";
8672
+ dispatch({ type: "send_message", message });
8673
+ if (shouldQueue) {
8674
+ pendingMessages.push({ message, options: sendOptions });
8675
+ return;
8676
+ }
8677
+ await sendToFlitro(message, sendOptions);
8678
+ },
8679
+ async abort(reason) {
8680
+ dispatch({ type: "abort", reason });
8681
+ pendingMessages.length = 0;
8682
+ await runtimeDriver?.abort?.(reason);
8683
+ },
8684
+ async respondToPermission(requestId, allowed, remember) {
8685
+ await getDriver().respondToPermission?.(requestId, allowed, remember);
8686
+ dispatch({ type: "permission_response" });
8687
+ },
8688
+ async resumeTool(runId, resumeData) {
8689
+ await getDriver().resumeTool?.(runId, resumeData);
8690
+ },
8691
+ async dispose() {
8692
+ dispatch({ type: "dispose" });
8693
+ pendingMessages.length = 0;
8694
+ sseStream?.disconnect();
8695
+ sseStream = void 0;
8696
+ await runtimeDriver?.dispose?.();
8697
+ }
8698
+ },
8699
+ async preflight() {
8700
+ dispatch({ type: "preflight_start" });
8701
+ if (!report.selected) {
8702
+ dispatch({ type: "preflight_error", error: report.error ?? "No runtime selected" });
8703
+ } else {
8704
+ dispatch({ type: "preflight_ok" });
8705
+ }
8706
+ const capItem = { type: "runtime_capability_report", report };
8707
+ const capEnvelope = {
8708
+ sessionId: resolvedSessionId,
8709
+ provider: "flitro",
8710
+ seq: timeline.length + 1,
8711
+ epoch,
8712
+ timestamp: now(),
8713
+ item: capItem
8714
+ };
8715
+ appendEnvelope(capEnvelope);
8716
+ return report;
8717
+ },
8718
+ async fetchTimeline(request = {}) {
8719
+ if (request.cursor?.epoch === epoch || !request.cursor) {
8720
+ const localResult = fetchTimeline(timeline, request);
8721
+ if (localResult.items.length > 0) return localResult;
8722
+ }
8723
+ if (resolvedSessionId) {
8724
+ try {
8725
+ const afterSeq = request.cursor?.afterSeq ?? 0;
8726
+ const result = await client.fetchTimeline(resolvedSessionId, afterSeq, request.limit);
8727
+ return {
8728
+ items: result.items,
8729
+ nextCursor: createTimelineCursor({
8730
+ epoch: result.nextCursor.epoch,
8731
+ afterSeq: result.nextCursor.afterSeq
8732
+ }),
8733
+ hasGap: result.hasGap
8734
+ };
8735
+ } catch {
8736
+ }
8737
+ }
8738
+ return {
8739
+ items: [],
8740
+ nextCursor: createTimelineCursor({ epoch, afterSeq: 0 }),
8741
+ hasGap: false
8742
+ };
8743
+ },
8744
+ getState() {
8745
+ return state;
8746
+ }
8747
+ };
8748
+ }
8749
+ function createFlitroEmbedRuntime(options) {
8750
+ return createFlitroProviderRuntime({
8751
+ server: {
8752
+ baseUrl: options.baseUrl,
8753
+ token: options.token,
8754
+ onTokenExpired: options.onTokenExpired
8755
+ },
8756
+ sessionId: options.sessionId,
8757
+ epoch: options.epoch,
8758
+ now: options.now,
8759
+ extensions: options.extensions,
8760
+ candidates: [{ kind: "app-server", available: true }],
8761
+ auth: { mode: "provider-owned", configured: true, source: "flitro-embed" }
8762
+ });
8763
+ }
8764
+ var PushTimelineStream = class {
8765
+ listeners = /* @__PURE__ */ new Set();
8766
+ connect(onEvent, onError, onClose) {
8767
+ this.listeners.add({ onEvent, onError, onClose });
8768
+ }
8769
+ disconnect() {
8770
+ for (const l of this.listeners) l.onClose?.();
8771
+ this.listeners.clear();
8772
+ }
8773
+ isConnected() {
8774
+ return this.listeners.size > 0;
8775
+ }
8776
+ emit(event) {
8777
+ for (const l of this.listeners) l.onEvent(event);
8778
+ }
8779
+ };
8780
+
8781
+ // ../packages/react/src/use-agent-session.ts
8782
+ function useAgentSession(options) {
8783
+ const onTokenExpiredRef = (0, import_react21.useRef)(options.onTokenExpired);
8784
+ onTokenExpiredRef.current = options.onTokenExpired;
8785
+ const tokenRef = (0, import_react21.useRef)(options.token);
8786
+ tokenRef.current = options.token;
8787
+ const runtime = (0, import_react21.useMemo)(() => {
8788
+ return createDeferredAgentRuntime({
8789
+ provider: "flitro",
8790
+ runtimeKind: "app-server",
8791
+ sessionId: options.sessionId,
8792
+ createRuntime: () => createFlitroEmbedRuntime({
8793
+ baseUrl: options.server,
8794
+ token: tokenRef.current,
8795
+ sessionId: options.sessionId,
8796
+ onTokenExpired: () => onTokenExpiredRef.current?.()
8797
+ })
8798
+ });
8799
+ }, [options.server, options.sessionId]);
8800
+ (0, import_react21.useEffect)(() => {
8801
+ return () => {
8802
+ void runtime.disposeIfCreated();
8803
+ };
8804
+ }, [runtime]);
8805
+ return { runtime, sessionId: options.sessionId, server: options.server };
8806
+ }
8807
+ function createDeferredAgentRuntime(options) {
8808
+ let runtime = null;
8809
+ let disposed = false;
8810
+ const getRuntime = () => {
8811
+ if (disposed) {
8812
+ throw new Error("Agent runtime has been disposed");
8813
+ }
8814
+ runtime ??= options.createRuntime();
8815
+ return runtime;
8816
+ };
8817
+ return {
8818
+ get sessionId() {
8819
+ return runtime?.sessionId ?? options.sessionId;
8820
+ },
8821
+ get provider() {
8822
+ return runtime?.provider ?? options.provider;
8823
+ },
8824
+ get runtimeKind() {
8825
+ return runtime?.runtimeKind ?? options.runtimeKind;
8826
+ },
8827
+ events: {
8828
+ connect(onEvent, onError, onClose) {
8829
+ getRuntime().events.connect(onEvent, onError, onClose);
8830
+ },
8831
+ disconnect() {
8832
+ runtime?.events.disconnect();
8833
+ },
8834
+ isConnected() {
8835
+ return runtime?.events.isConnected() ?? false;
8836
+ }
8837
+ },
8838
+ commands: {
8839
+ sendMessage(message, sendOptions) {
8840
+ return getRuntime().commands.sendMessage(message, sendOptions);
8841
+ },
8842
+ abort(reason) {
8843
+ return getRuntime().commands.abort(reason);
8844
+ },
8845
+ respondToPermission(requestId, allowed, remember) {
8846
+ return getRuntime().commands.respondToPermission(requestId, allowed, remember);
8847
+ },
8848
+ async resumeTool(runId, resumeData) {
8849
+ const commands = getRuntime().commands;
8850
+ if (!commands.resumeTool) {
8851
+ throw new Error(`resumeTool is not supported by the ${options.provider} runtime`);
8852
+ }
8853
+ await commands.resumeTool(runId, resumeData);
8854
+ },
8855
+ dispose() {
8856
+ disposed = true;
8857
+ const current = runtime;
8858
+ runtime = null;
8859
+ return current?.commands.dispose() ?? Promise.resolve();
8860
+ }
8861
+ },
8862
+ preflight() {
8863
+ return getRuntime().preflight();
8864
+ },
8865
+ fetchTimeline(request) {
8866
+ return getRuntime().fetchTimeline(request);
8867
+ },
8868
+ getState() {
8869
+ return runtime?.getState() ?? {
8870
+ status: initialRuntimeState.status,
8871
+ acceptedMessages: [],
8872
+ queuedMessages: []
8873
+ };
8874
+ },
8875
+ async disposeIfCreated() {
8876
+ if (!runtime) return;
8877
+ const current = runtime;
8878
+ runtime = null;
8879
+ await current.commands.dispose();
8880
+ }
8881
+ };
8882
+ }
7904
8883
  // Annotate the CommonJS export names for ESM import in node:
7905
8884
  0 && (module.exports = {
7906
8885
  ActivityDetailsPanel,