la-machina-engine 0.19.6 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2098,6 +2098,7 @@ var AnthropicClient = class {
2098
2098
  if (betas.length > 0) {
2099
2099
  requestOptions.headers = { "anthropic-beta": betas.join(",") };
2100
2100
  }
2101
+ if (request.abortSignal !== void 0) requestOptions.signal = request.abortSignal;
2101
2102
  let stream;
2102
2103
  try {
2103
2104
  stream = this.sdk.messages.stream(params, requestOptions);
@@ -2284,6 +2285,7 @@ var AISdkAdapter = class {
2284
2285
  tools,
2285
2286
  ...request.maxTokens !== void 0 ? { maxOutputTokens: request.maxTokens } : {},
2286
2287
  ...request.temperature !== void 0 ? { temperature: request.temperature } : {},
2288
+ ...request.abortSignal !== void 0 ? { abortSignal: request.abortSignal } : {},
2287
2289
  // Plan 025 — pass through `'required'` so the AI SDK forwards it
2288
2290
  // to the provider as that provider's "force tool call" flag.
2289
2291
  ...request.toolChoice === "required" ? { toolChoice: "required" } : {},
@@ -3305,6 +3307,9 @@ async function emitInspectTurn(args) {
3305
3307
  ...cacheRead !== void 0 ? { cacheReadInput: cacheRead } : {}
3306
3308
  });
3307
3309
  }
3310
+ function isAbortSignalAborted(signal) {
3311
+ return signal?.aborted === true;
3312
+ }
3308
3313
  var DEFAULT_COMPACTION = {
3309
3314
  strategy: "drop-middle",
3310
3315
  threshold: 0.85,
@@ -3457,6 +3462,7 @@ async function agentLoop(options) {
3457
3462
  messages: normalizedMessages,
3458
3463
  system,
3459
3464
  tools: anthropicTools,
3465
+ ...options.runSignal !== void 0 ? { abortSignal: options.runSignal } : {},
3460
3466
  ...escalatedMaxTokens !== void 0 ? { maxTokens: escalatedMaxTokens } : {},
3461
3467
  ...options.toolChoice !== void 0 ? { toolChoice: options.toolChoice } : {}
3462
3468
  })) {
@@ -3470,6 +3476,9 @@ async function agentLoop(options) {
3470
3476
  }
3471
3477
  }
3472
3478
  } catch (err) {
3479
+ if (isAbortSignalAborted(options.runSignal)) {
3480
+ return failed(new RunTimeoutError(options.runTimeoutMs ?? 0), transcript);
3481
+ }
3473
3482
  if (isPromptTooLong(err) && !compactedThisTurn) {
3474
3483
  compactedThisTurn = true;
3475
3484
  const emergency = await compactIfNeeded({
@@ -9069,6 +9078,123 @@ function describe(svc) {
9069
9078
  endpoints
9070
9079
  };
9071
9080
  }
9081
+ function describeEndpoint(ep) {
9082
+ return {
9083
+ method: ep.method,
9084
+ path: ep.path,
9085
+ description: ep.description,
9086
+ ...ep.inputSchema !== void 0 ? { inputSchema: ep.inputSchema } : {},
9087
+ ...ep.outputHint !== void 0 ? { outputHint: ep.outputHint } : {},
9088
+ ...ep.pagination !== void 0 ? { pagination: ep.pagination } : {},
9089
+ ...ep.response !== void 0 ? { response: ep.response } : {}
9090
+ };
9091
+ }
9092
+ var DEFAULT_SEARCH_LIMIT = 8;
9093
+ var MAX_SEARCH_LIMIT = 20;
9094
+ var FIELD_WEIGHTS = {
9095
+ path: 3,
9096
+ method: 2,
9097
+ description: 2,
9098
+ inputKey: 1,
9099
+ outputHint: 1,
9100
+ response: 1
9101
+ };
9102
+ function tokenize(input) {
9103
+ return input.toLowerCase().split(/[\s/_-]+/).map((t) => t.trim()).filter((t) => t.length > 0);
9104
+ }
9105
+ function isObject(v) {
9106
+ return typeof v === "object" && v !== null && !Array.isArray(v);
9107
+ }
9108
+ function scoreEndpointMatch(ep, tokens, opts) {
9109
+ let score = 0;
9110
+ const matched = /* @__PURE__ */ new Set();
9111
+ const pathLower = ep.path.toLowerCase();
9112
+ const methodLower = ep.method.toLowerCase();
9113
+ const descriptionLower = ep.description.toLowerCase();
9114
+ const outputHintLower = ep.outputHint?.toLowerCase() ?? "";
9115
+ const responseBlob = [ep.response?.itemsPath, ep.response?.totalPath, ep.pagination?.mode].filter((s) => typeof s === "string" && s.length > 0).join(" ").toLowerCase();
9116
+ const inputKeys = [];
9117
+ if (isObject(ep.inputSchema)) {
9118
+ const props = ep.inputSchema.properties;
9119
+ if (isObject(props)) {
9120
+ for (const key of Object.keys(props)) inputKeys.push(key);
9121
+ }
9122
+ }
9123
+ const inputKeysLower = inputKeys.map((k) => k.toLowerCase());
9124
+ for (const t of tokens) {
9125
+ if (pathLower.includes(t)) {
9126
+ score += FIELD_WEIGHTS.path;
9127
+ matched.add("path");
9128
+ }
9129
+ if (opts.pathOnly) continue;
9130
+ if (t === methodLower) {
9131
+ score += FIELD_WEIGHTS.method;
9132
+ matched.add("method");
9133
+ }
9134
+ if (descriptionLower.includes(t)) {
9135
+ score += FIELD_WEIGHTS.description;
9136
+ matched.add("description");
9137
+ }
9138
+ if (inputKeysLower.some((k) => k.includes(t))) {
9139
+ score += FIELD_WEIGHTS.inputKey;
9140
+ matched.add("inputKey");
9141
+ }
9142
+ if (outputHintLower.length > 0 && outputHintLower.includes(t)) {
9143
+ score += FIELD_WEIGHTS.outputHint;
9144
+ matched.add("outputHint");
9145
+ }
9146
+ if (responseBlob.length > 0 && responseBlob.includes(t)) {
9147
+ score += FIELD_WEIGHTS.response;
9148
+ matched.add("response");
9149
+ }
9150
+ }
9151
+ if (score === 0) return { score: 0, matched: [] };
9152
+ return {
9153
+ score,
9154
+ matched: [...matched].sort(),
9155
+ ...inputKeys.length > 0 ? { inputKeys } : {}
9156
+ };
9157
+ }
9158
+ function searchService(svc, opts) {
9159
+ const tokens = tokenize(opts.query);
9160
+ const endpoints = svc.endpoints ?? [];
9161
+ const scored = [];
9162
+ for (const ep of endpoints) {
9163
+ if (opts.method !== void 0 && ep.method !== opts.method) continue;
9164
+ const r = scoreEndpointMatch(ep, tokens, { pathOnly: opts.pathOnly });
9165
+ if (r.score === 0) continue;
9166
+ scored.push({
9167
+ ep,
9168
+ score: r.score,
9169
+ matched: r.matched,
9170
+ ...r.inputKeys !== void 0 ? { inputKeys: r.inputKeys } : {}
9171
+ });
9172
+ }
9173
+ scored.sort((a, b) => {
9174
+ if (b.score !== a.score) return b.score - a.score;
9175
+ if (a.ep.method !== b.ep.method) {
9176
+ return a.ep.method < b.ep.method ? -1 : 1;
9177
+ }
9178
+ if (a.ep.path !== b.ep.path) return a.ep.path < b.ep.path ? -1 : 1;
9179
+ return 0;
9180
+ });
9181
+ const truncated = scored.length > opts.limit;
9182
+ const slice = scored.slice(0, opts.limit);
9183
+ return {
9184
+ service: svc.name,
9185
+ query: opts.query,
9186
+ matches: slice.map((s) => ({
9187
+ method: s.ep.method,
9188
+ path: s.ep.path,
9189
+ description: s.ep.description,
9190
+ score: s.score,
9191
+ matched: s.matched,
9192
+ ...s.inputKeys !== void 0 && s.inputKeys.length > 0 ? { inputKeys: s.inputKeys } : {},
9193
+ ...s.ep.outputHint !== void 0 ? { outputHint: s.ep.outputHint } : {}
9194
+ })),
9195
+ ...truncated ? { truncated: true } : {}
9196
+ };
9197
+ }
9072
9198
  function hasDescribableEndpoints(svc) {
9073
9199
  return svc.endpoints !== void 0 && svc.endpoints.length > 0;
9074
9200
  }
@@ -9084,18 +9210,39 @@ function createDescribeServiceTool(opts) {
9084
9210
  const serviceNames = [...serviceMap.keys()];
9085
9211
  const cache = /* @__PURE__ */ new Map();
9086
9212
  const inputSchema19 = import_zod24.z.object({
9087
- service: import_zod24.z.enum(serviceNames)
9213
+ service: import_zod24.z.enum(serviceNames),
9214
+ /**
9215
+ * Plan 054 — free-text query. When set with no `path`, returns
9216
+ * the compact ranked-match shape. Must be a non-empty string.
9217
+ */
9218
+ query: import_zod24.z.string().min(1).optional(),
9219
+ /**
9220
+ * Plan 054 — narrow ranked matches to one HTTP verb. Also used
9221
+ * by the exact-endpoint branch in combination with `path`.
9222
+ */
9223
+ method: import_zod24.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional(),
9224
+ /**
9225
+ * Plan 054 — paired with `method` returns full endpoint detail;
9226
+ * paired without `method` runs a path-only ranked search.
9227
+ */
9228
+ path: import_zod24.z.string().min(1).optional(),
9229
+ /**
9230
+ * Plan 054 — defaults to 8, capped at 20. Only meaningful in
9231
+ * search modes; ignored when fetching the full catalog or one
9232
+ * exact endpoint.
9233
+ */
9234
+ limit: import_zod24.z.number().int().min(1).max(MAX_SEARCH_LIMIT).optional()
9088
9235
  });
9089
- const description = `Look up the endpoint catalog for one configured API service. Returns every endpoint's method, path, description, input schema, and (when declared) pagination + response-extraction metadata. Call this before invoking \`ApiCall\` when the service has lazy endpoints; for paginated list endpoints, prefer \`ApiCall\` with \`pagination.auto: true\` over manually issuing one call per page. Services: ${serviceNames.join(", ")}.`;
9236
+ const description = `Look up endpoints on one configured API service.
9237
+ 1. Search: DescribeService({ service, query }) returns compact ranked matches with method, path, description, and a relevance score. Use this for broad services where only one endpoint slice is needed. Add { method } to filter.
9238
+ 2. Detail: DescribeService({ service, method, path }) returns full endpoint detail including inputSchema. Call this after a search has picked a candidate.
9239
+ 3. Execute: ApiCall(...) with the chosen method, path, and inputs.
9240
+ Legacy: DescribeService({ service }) returns the full endpoint catalog \u2014 expensive for large services and discouraged for new code. Services: ${serviceNames.join(", ")}.`;
9090
9241
  return defineTool({
9091
9242
  name: opts.toolName ?? "DescribeService",
9092
9243
  description,
9093
9244
  inputSchema: inputSchema19,
9094
9245
  execute: async (input) => {
9095
- const cached = cache.get(input.service);
9096
- if (cached !== void 0) {
9097
- return { content: JSON.stringify(cached), metadata: { cached: true } };
9098
- }
9099
9246
  const svc = serviceMap.get(input.service);
9100
9247
  if (!svc) {
9101
9248
  return {
@@ -9103,6 +9250,56 @@ function createDescribeServiceTool(opts) {
9103
9250
  isError: true
9104
9251
  };
9105
9252
  }
9253
+ const limit = Math.min(input.limit ?? DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT);
9254
+ if (input.method !== void 0 && input.path !== void 0) {
9255
+ const ep = (svc.endpoints ?? []).find(
9256
+ (e) => e.method === input.method && e.path === input.path
9257
+ );
9258
+ if (ep === void 0) {
9259
+ return {
9260
+ content: JSON.stringify({
9261
+ code: "ENDPOINT_NOT_FOUND",
9262
+ service: input.service,
9263
+ method: input.method,
9264
+ path: input.path,
9265
+ message: `No endpoint ${input.method} ${input.path} on service "${input.service}". Call DescribeService with { service, query } to search the catalog.`
9266
+ }),
9267
+ isError: true
9268
+ };
9269
+ }
9270
+ return {
9271
+ content: JSON.stringify(describeEndpoint(ep)),
9272
+ metadata: { mode: "detail" }
9273
+ };
9274
+ }
9275
+ if (input.query !== void 0) {
9276
+ const result = searchService(svc, {
9277
+ query: input.query,
9278
+ ...input.method !== void 0 ? { method: input.method } : {},
9279
+ limit,
9280
+ pathOnly: false
9281
+ });
9282
+ return {
9283
+ content: JSON.stringify(result),
9284
+ metadata: { mode: "search" }
9285
+ };
9286
+ }
9287
+ if (input.path !== void 0) {
9288
+ const result = searchService(svc, {
9289
+ query: input.path,
9290
+ ...input.method !== void 0 ? { method: input.method } : {},
9291
+ limit,
9292
+ pathOnly: true
9293
+ });
9294
+ return {
9295
+ content: JSON.stringify(result),
9296
+ metadata: { mode: "search" }
9297
+ };
9298
+ }
9299
+ const cached = cache.get(input.service);
9300
+ if (cached !== void 0) {
9301
+ return { content: JSON.stringify(cached), metadata: { cached: true } };
9302
+ }
9106
9303
  const payload = describe(svc);
9107
9304
  cache.set(input.service, payload);
9108
9305
  return { content: JSON.stringify(payload), metadata: { cached: false } };
@@ -9179,7 +9376,7 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
9179
9376
  "he",
9180
9377
  "she"
9181
9378
  ]);
9182
- function tokenize(text2) {
9379
+ function tokenize2(text2) {
9183
9380
  if (typeof text2 !== "string" || text2.length === 0) return [];
9184
9381
  const seen = /* @__PURE__ */ new Set();
9185
9382
  for (const raw of text2.toLowerCase().split(/[\W_]+/)) {
@@ -9294,7 +9491,7 @@ function splitSections(content, relPath) {
9294
9491
  heading: "",
9295
9492
  slug: `${relPath}#`,
9296
9493
  depth: 0,
9297
- words: tokenize(leadInBody),
9494
+ words: tokenize2(leadInBody),
9298
9495
  preview: makePreview(leadInBody),
9299
9496
  startLine: 1,
9300
9497
  endLine: leadInEndLine
@@ -9310,7 +9507,7 @@ function splitSections(content, relPath) {
9310
9507
  heading: h.heading,
9311
9508
  slug: `${relPath}#${slugify(h.heading)}`,
9312
9509
  depth: h.depth,
9313
- words: tokenize(body),
9510
+ words: tokenize2(body),
9314
9511
  preview: makePreview(body),
9315
9512
  startLine,
9316
9513
  endLine
@@ -9427,7 +9624,7 @@ function createSearchKnowledgeTool(opts) {
9427
9624
  inputSchema: inputSchema17,
9428
9625
  execute: async ({ query, maxResults }) => {
9429
9626
  const limit = Math.min(maxResults ?? cap, cap);
9430
- const queryTokens = tokenize(query);
9627
+ const queryTokens = tokenize2(query);
9431
9628
  if (queryTokens.length === 0) {
9432
9629
  return { content: "no searchable tokens in query", isError: false };
9433
9630
  }
@@ -9452,7 +9649,7 @@ function createSearchKnowledgeTool(opts) {
9452
9649
  }
9453
9650
  const externalHits = [];
9454
9651
  for (const link of externals) {
9455
- const score = scoreOverlap(tokenize(link.description), queryTokens);
9652
+ const score = scoreOverlap(tokenize2(link.description), queryTokens);
9456
9653
  if (score > 0) externalHits.push({ link, score });
9457
9654
  }
9458
9655
  const all = [