orchestrator-client 5.7.4 → 5.7.10

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/index.js CHANGED
@@ -171,6 +171,11 @@ var OrchestratorAsync = class {
171
171
  _makeUrl(path) {
172
172
  return `${this._baseUrl}${path}`;
173
173
  }
174
+ _makeAbsoluteUrl(path) {
175
+ const raw = this._makeUrl(path);
176
+ const base = typeof globalThis.location !== "undefined" ? globalThis.location.href : void 0;
177
+ return new URL(raw, base);
178
+ }
174
179
  async _resolveHeaders() {
175
180
  const headers = {};
176
181
  if (this._apiKey) {
@@ -187,7 +192,7 @@ var OrchestratorAsync = class {
187
192
  return headers;
188
193
  }
189
194
  async _request(method, path, opts) {
190
- const url = new URL(this._makeUrl(path));
195
+ const url = this._makeAbsoluteUrl(path);
191
196
  if (opts?.params) {
192
197
  for (const [key, value] of Object.entries(opts.params)) {
193
198
  if (value !== void 0) {
@@ -981,7 +986,7 @@ var OrchestratorAsync = class {
981
986
  // ------------------------------------------------------------------
982
987
  async listErrors(params) {
983
988
  const authHeaders = await this._resolveHeaders();
984
- const url = new URL(this._makeUrl("/errors"));
989
+ const url = this._makeAbsoluteUrl("/errors");
985
990
  if (params?.page !== void 0)
986
991
  url.searchParams.set("page", String(params.page));
987
992
  if (params?.limit !== void 0)
@@ -2060,9 +2065,302 @@ var RealtimeClient = class {
2060
2065
  }
2061
2066
  };
2062
2067
 
2068
+ // src/flow.ts
2069
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
2070
+ "completed",
2071
+ "failed",
2072
+ "cancelled",
2073
+ "translation"
2074
+ ]);
2075
+ var DEFAULT_FLOW_TIMEOUT_MS = 6e5;
2076
+ var _defaultClient;
2077
+ var _defaultRealtime;
2078
+ function setupDefaultClient(client, realtime) {
2079
+ _defaultClient = client;
2080
+ _defaultRealtime = realtime;
2081
+ }
2082
+ function getDefaultClient() {
2083
+ if (!_defaultClient) {
2084
+ throw new Error(
2085
+ "No default OrchestratorAsync set. Call setupDefaultClient() during application startup, or pass client to flow.run()."
2086
+ );
2087
+ }
2088
+ return _defaultClient;
2089
+ }
2090
+ function getDefaultRealtime() {
2091
+ return _defaultRealtime;
2092
+ }
2093
+ var FlowError = class extends Error {
2094
+ constructor(message) {
2095
+ super(message);
2096
+ this.name = "FlowError";
2097
+ }
2098
+ };
2099
+ var FlowTimeoutError = class extends FlowError {
2100
+ constructor(message) {
2101
+ super(message);
2102
+ this.name = "FlowTimeoutError";
2103
+ }
2104
+ };
2105
+ var FlowCancelledError = class extends FlowError {
2106
+ constructor(taskId) {
2107
+ super(`Task ${taskId} was cancelled`);
2108
+ this.name = "FlowCancelledError";
2109
+ this.taskId = taskId;
2110
+ }
2111
+ };
2112
+ function extractJsonFromMessage(content) {
2113
+ const cleaned = content.replace(/^\ufeff|\u200b|\u200c|\u200d/, "").trim();
2114
+ if (cleaned.startsWith("{")) {
2115
+ try {
2116
+ return JSON.parse(cleaned);
2117
+ } catch {
2118
+ }
2119
+ }
2120
+ const start = content.indexOf("{");
2121
+ const end = content.lastIndexOf("}");
2122
+ if (start !== -1 && end !== -1 && end > start) {
2123
+ try {
2124
+ return JSON.parse(content.slice(start, end + 1));
2125
+ } catch {
2126
+ }
2127
+ }
2128
+ const jsonFenceMatch = content.match(/```(?:json)\s*\n([\s\S]*?)\n```/i);
2129
+ if (jsonFenceMatch) {
2130
+ try {
2131
+ return JSON.parse(jsonFenceMatch[1].trim());
2132
+ } catch {
2133
+ }
2134
+ }
2135
+ const anyFenceMatch = content.match(/```(?:[\w]*)\s*\n([\s\S]*?)\n```/);
2136
+ if (anyFenceMatch) {
2137
+ try {
2138
+ return JSON.parse(anyFenceMatch[1].trim());
2139
+ } catch {
2140
+ }
2141
+ }
2142
+ const bareBraceMatch = content.match(/\{[\s\S]*\}/);
2143
+ if (bareBraceMatch) {
2144
+ try {
2145
+ return JSON.parse(bareBraceMatch[0]);
2146
+ } catch {
2147
+ }
2148
+ }
2149
+ throw new FlowError(
2150
+ "Could not extract a valid JSON object from the agent's final message. The agent did not format its answer as required."
2151
+ );
2152
+ }
2153
+ var Flow = class {
2154
+ constructor() {
2155
+ // -- Workflow-level defaults (override in subclass) --------------------
2156
+ /** Orchestrator workflow type — `"proactive"` by default. */
2157
+ this.workflowId = "proactive";
2158
+ /** Maximum agent turns before the orchestrator forces a failure. */
2159
+ this.maxIterations = 100;
2160
+ /** LLM reasoning budget: `"low"`, `"medium"`, or `"high"`. */
2161
+ this.reasoningEffort = "medium";
2162
+ /**
2163
+ * Maximum milliseconds to wait for the orchestrator task to reach a
2164
+ * terminal state. When exceeded, {@link FlowTimeoutError} is raised.
2165
+ */
2166
+ this.flowTimeoutMs = DEFAULT_FLOW_TIMEOUT_MS;
2167
+ }
2168
+ /**
2169
+ * Optional system-prompt override.
2170
+ * When set, this replaces the orchestrator's default system prompt.
2171
+ */
2172
+ get systemPrompt() {
2173
+ return void 0;
2174
+ }
2175
+ /**
2176
+ * Optional developer-prompt override appended after system prompt.
2177
+ */
2178
+ get developerPrompt() {
2179
+ return void 0;
2180
+ }
2181
+ /**
2182
+ * Restrict which MCP / built-in tools the agent may use.
2183
+ * `undefined` means *all* tools are available. An empty array means
2184
+ * *no* tools — text-only reasoning.
2185
+ */
2186
+ get availableTools() {
2187
+ return void 0;
2188
+ }
2189
+ /**
2190
+ * Per-task feature toggles sent in the creation request.
2191
+ * By default summaries and translation are disabled since flow
2192
+ * output is typically machine-consumed, not human-read.
2193
+ * Override in subclasses that produce human-facing content.
2194
+ */
2195
+ get taskOptions() {
2196
+ return { disableSummaries: true, disableTranslation: true };
2197
+ }
2198
+ /**
2199
+ * Override the agent model for this flow.
2200
+ */
2201
+ get agentModelId() {
2202
+ return void 0;
2203
+ }
2204
+ /**
2205
+ * Override the orchestrator (validation) model for this flow.
2206
+ */
2207
+ get orchestratorModelId() {
2208
+ return void 0;
2209
+ }
2210
+ /**
2211
+ * Cancel the underlying orchestrator task, if one is running.
2212
+ *
2213
+ * Calling `cancel()` after `run()` has returned is a no-op.
2214
+ * The `run()` promise will reject with {@link FlowCancelledError}
2215
+ * after the orchestrator task transitions to `"cancelled"`.
2216
+ */
2217
+ async cancel(client) {
2218
+ if (!this._lastTaskId) return;
2219
+ const resolvedClient = client ?? getDefaultClient();
2220
+ await resolvedClient.cancelTask(this._lastTaskId);
2221
+ }
2222
+ // -- Lifecycle ---------------------------------------------------------
2223
+ /**
2224
+ * Execute the flow end-to-end.
2225
+ *
2226
+ * @returns The parsed result.
2227
+ * @throws {FlowError} If the task fails, times out, or cannot be parsed.
2228
+ */
2229
+ async run(params) {
2230
+ const client = params?.client ?? getDefaultClient();
2231
+ const realtime = params?.realtime ?? getDefaultRealtime();
2232
+ const timeoutMs = params?.timeoutMs ?? this.flowTimeoutMs;
2233
+ console.log(
2234
+ `[Flow] Starting ${this.constructor.name} \u2014 workflow=${this.workflowId} goal=${this.goalPrompt.slice(0, 80)}`
2235
+ );
2236
+ const response = await client.createTask({
2237
+ workflowId: this.workflowId,
2238
+ goalPrompt: this.goalPrompt,
2239
+ systemPrompt: this.systemPrompt,
2240
+ developerPrompt: this.developerPrompt,
2241
+ availableTools: this.availableTools,
2242
+ options: this.taskOptions,
2243
+ maxIterations: this.maxIterations,
2244
+ reasoningEffort: this.reasoningEffort,
2245
+ agentModelId: this.agentModelId,
2246
+ orchestratorModelId: this.orchestratorModelId
2247
+ });
2248
+ this._lastTaskId = response.taskId;
2249
+ const taskId = response.taskId;
2250
+ console.log(
2251
+ `[Flow] Task created \u2014 taskId=${taskId} status=${response.status}`
2252
+ );
2253
+ const status = await this._waitForTerminal(client, taskId, {
2254
+ realtime,
2255
+ timeoutMs
2256
+ });
2257
+ if (status.status === "cancelled") {
2258
+ throw new FlowCancelledError(taskId);
2259
+ }
2260
+ const finalMessage = await this._getFinalMessage(client, taskId);
2261
+ console.log(
2262
+ `[Flow] ${this.constructor.name} completed \u2014 taskId=${taskId} messageLen=${finalMessage.length}`
2263
+ );
2264
+ return this.parseResult(finalMessage);
2265
+ }
2266
+ // -- Completion waiting -----------------------------------------------
2267
+ async _waitForTerminal(client, taskId, opts) {
2268
+ const { realtime, timeoutMs } = opts;
2269
+ const status = await client.getTaskStatus(taskId);
2270
+ if (TERMINAL_STATUSES.has(status.status)) {
2271
+ console.log(
2272
+ `[Flow] Task ${taskId} already terminal \u2014 status=${status.status}`
2273
+ );
2274
+ return status;
2275
+ }
2276
+ if (realtime) {
2277
+ await this._waitViaSocketIO(taskId, realtime, timeoutMs);
2278
+ } else {
2279
+ console.log(
2280
+ `[Flow] No RealtimeClient available \u2014 polling task ${taskId} with backoff (timeout=${timeoutMs}ms)`
2281
+ );
2282
+ await this._waitViaPolling(client, taskId, timeoutMs);
2283
+ }
2284
+ return client.getTaskStatus(taskId);
2285
+ }
2286
+ async _waitViaSocketIO(taskId, realtime, timeoutMs) {
2287
+ realtime.subscribeTask(taskId);
2288
+ return new Promise((resolve, reject) => {
2289
+ const timer = setTimeout(() => {
2290
+ cleanup();
2291
+ reject(
2292
+ new FlowTimeoutError(
2293
+ `Task ${taskId} did not reach a terminal state within ${timeoutMs}ms`
2294
+ )
2295
+ );
2296
+ }, timeoutMs);
2297
+ const handler = (...args) => {
2298
+ const event = args[0] ?? {};
2299
+ if (event.task_id !== taskId) return;
2300
+ const newStatus = event.new_status ?? "";
2301
+ if (TERMINAL_STATUSES.has(newStatus)) {
2302
+ cleanup();
2303
+ resolve();
2304
+ }
2305
+ };
2306
+ const cleanup = () => {
2307
+ clearTimeout(timer);
2308
+ realtime.off("task_status_changed", handler);
2309
+ realtime.unsubscribeTask(taskId);
2310
+ };
2311
+ realtime.on("task_status_changed", handler);
2312
+ });
2313
+ }
2314
+ async _waitViaPolling(client, taskId, timeoutMs) {
2315
+ const deadline = Date.now() + timeoutMs;
2316
+ let delay = 1e3;
2317
+ while (Date.now() < deadline) {
2318
+ const status = await client.getTaskStatus(taskId);
2319
+ if (TERMINAL_STATUSES.has(status.status)) return;
2320
+ await this._sleep(delay);
2321
+ delay = Math.min(Math.round(delay * 1.5), 1e4);
2322
+ }
2323
+ throw new FlowTimeoutError(
2324
+ `Task ${taskId} did not reach a terminal state within ${timeoutMs}ms (polling fallback)`
2325
+ );
2326
+ }
2327
+ // -- Conversation helpers ---------------------------------------------
2328
+ async _getFinalMessage(client, taskId) {
2329
+ const conversation = await client.getTaskConversation(taskId, {
2330
+ includeSummaries: false,
2331
+ excludeArchived: false
2332
+ });
2333
+ const messages = conversation.conversation;
2334
+ for (let i = messages.length - 1; i >= 0; i--) {
2335
+ const msg = messages[i];
2336
+ if (msg.role === "assistant" && msg.content?.trim()) {
2337
+ return msg.content;
2338
+ }
2339
+ }
2340
+ for (let i = messages.length - 1; i >= 0; i--) {
2341
+ const msg = messages[i];
2342
+ if (msg.role === "assistant" && msg.archived && msg.id != null) {
2343
+ const archived = await client.getArchivedMessageContent(taskId, msg.id);
2344
+ if (archived.content?.trim()) {
2345
+ return archived.content;
2346
+ }
2347
+ }
2348
+ }
2349
+ const status = await client.getTaskStatus(taskId);
2350
+ throw new FlowError(
2351
+ `No assistant message with content found in task ${taskId}. Status: ${status.status}. Result: ${(status.result ?? "").slice(0, 200) || "empty"}. Messages in conversation: ${messages.length}.`
2352
+ );
2353
+ }
2354
+ // -- Helper -----------------------------------------------------------
2355
+ _sleep(ms) {
2356
+ return new Promise((resolve) => setTimeout(resolve, ms));
2357
+ }
2358
+ };
2359
+
2063
2360
  // src/index.ts
2064
2361
  var VERSION = "5.6.0";
2065
2362
  export {
2363
+ DEFAULT_FLOW_TIMEOUT_MS,
2066
2364
  EVENT_ERROR_EVENT_RECORDED,
2067
2365
  EVENT_MESSAGE_ADDED,
2068
2366
  EVENT_MESSAGE_STREAMING,
@@ -2074,6 +2372,10 @@ export {
2074
2372
  EVENT_TASK_ITERATION_CHANGED,
2075
2373
  EVENT_TASK_RESULT_UPDATED,
2076
2374
  EVENT_TASK_STATUS_CHANGED,
2375
+ Flow,
2376
+ FlowCancelledError,
2377
+ FlowError,
2378
+ FlowTimeoutError,
2077
2379
  Orchestrator,
2078
2380
  OrchestratorAPIError,
2079
2381
  OrchestratorAsync,
@@ -2087,7 +2389,9 @@ export {
2087
2389
  camelToSnake,
2088
2390
  createInsecureFetch,
2089
2391
  deepCamelCase,
2392
+ extractJsonFromMessage,
2090
2393
  loadConfig,
2394
+ setupDefaultClient,
2091
2395
  snakeToCamel
2092
2396
  };
2093
2397
  //# sourceMappingURL=index.js.map