@elevasis/sdk 1.44.0 → 1.44.2

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/cli.cjs CHANGED
@@ -44117,17 +44117,17 @@ function getLeadGenCrmHandoffResourceIds(model) {
44117
44117
  (resource) => resource.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && resource.ontology?.usesCatalogs?.includes(CRM_PIPELINE_CATALOG_ONTOLOGY_ID) === true
44118
44118
  ).map((resource) => resource.id);
44119
44119
  }
44120
- function getSystemInterfaceReadinessMarker(model, request) {
44121
- const system = getSystem(model, request.systemPath);
44120
+ function getSystemInterfaceReadinessMarker(model, request2) {
44121
+ const system = getSystem(model, request2.systemPath);
44122
44122
  if (system === void 0) return void 0;
44123
- if (request.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && request.interfaceKey === LEAD_GEN_CRM_HANDOFF_INTERFACE.interfaceKey) {
44123
+ if (request2.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && request2.interfaceKey === LEAD_GEN_CRM_HANDOFF_INTERFACE.interfaceKey) {
44124
44124
  return {
44125
44125
  lifecycle: "active",
44126
44126
  readinessProfile: LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile,
44127
44127
  resourceIds: getLeadGenCrmHandoffResourceIds(model)
44128
44128
  };
44129
44129
  }
44130
- if (request.interfaceKey === "api" && system.apiInterface !== void 0) {
44130
+ if (request2.interfaceKey === "api" && system.apiInterface !== void 0) {
44131
44131
  return system.apiInterface;
44132
44132
  }
44133
44133
  return void 0;
@@ -44186,24 +44186,24 @@ function requireContractReadiness(issues, index, resources, contract, context) {
44186
44186
  requireScopedBinding(issues, catalogs, "usesCatalogs", catalogId, context);
44187
44187
  }
44188
44188
  }
44189
- function computeInterfaceReadiness(model, request) {
44189
+ function computeInterfaceReadiness(model, request2) {
44190
44190
  const issues = [];
44191
- const system = getSystem(model, request.systemPath);
44192
- const systemInterface = getSystemInterfaceReadinessMarker(model, request);
44193
- const readinessProfile = systemInterface === void 0 ? void 0 : profileForInterface(request.systemPath, request.interfaceKey, systemInterface.readinessProfile);
44191
+ const system = getSystem(model, request2.systemPath);
44192
+ const systemInterface = getSystemInterfaceReadinessMarker(model, request2);
44193
+ const readinessProfile = systemInterface === void 0 ? void 0 : profileForInterface(request2.systemPath, request2.interfaceKey, systemInterface.readinessProfile);
44194
44194
  const scopedResourceIds = systemInterface?.resourceIds ?? [];
44195
44195
  if (system === void 0) {
44196
44196
  addReadinessIssue(
44197
44197
  issues,
44198
44198
  "SYSTEM_INTERFACE_MISSING",
44199
44199
  "missing-system",
44200
- `System "${request.systemPath}" is missing.`,
44201
- { ref: request.systemPath }
44200
+ `System "${request2.systemPath}" is missing.`,
44201
+ { ref: request2.systemPath }
44202
44202
  );
44203
44203
  return {
44204
44204
  ready: false,
44205
- systemPath: request.systemPath,
44206
- interfaceKey: request.interfaceKey,
44205
+ systemPath: request2.systemPath,
44206
+ interfaceKey: request2.interfaceKey,
44207
44207
  scopedResourceIds,
44208
44208
  issues
44209
44209
  };
@@ -44213,13 +44213,13 @@ function computeInterfaceReadiness(model, request) {
44213
44213
  issues,
44214
44214
  "SYSTEM_INTERFACE_MISSING",
44215
44215
  "missing-interface",
44216
- `System "${request.systemPath}" does not declare interface "${request.interfaceKey}".`,
44217
- { path: readinessMarkerPath(request) }
44216
+ `System "${request2.systemPath}" does not declare interface "${request2.interfaceKey}".`,
44217
+ { path: readinessMarkerPath(request2) }
44218
44218
  );
44219
44219
  return {
44220
44220
  ready: false,
44221
- systemPath: request.systemPath,
44222
- interfaceKey: request.interfaceKey,
44221
+ systemPath: request2.systemPath,
44222
+ interfaceKey: request2.interfaceKey,
44223
44223
  scopedResourceIds,
44224
44224
  issues
44225
44225
  };
@@ -44229,11 +44229,11 @@ function computeInterfaceReadiness(model, request) {
44229
44229
  issues,
44230
44230
  "SYSTEM_INTERFACE_DISABLED",
44231
44231
  "inactive-interface",
44232
- `System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" lifecycle is "${systemInterface.lifecycle}".`,
44233
- { path: `${readinessMarkerPath(request)}.lifecycle` }
44232
+ `System Interface "${formatInterfaceIdentity(request2.systemPath, request2.interfaceKey)}" lifecycle is "${systemInterface.lifecycle}".`,
44233
+ { path: `${readinessMarkerPath(request2)}.lifecycle` }
44234
44234
  );
44235
44235
  }
44236
- const checkedReadinessProfile = readinessProfile ?? profileForInterface(request.systemPath, request.interfaceKey);
44236
+ const checkedReadinessProfile = readinessProfile ?? profileForInterface(request2.systemPath, request2.interfaceKey);
44237
44237
  const isBuiltIn = isBuiltInReadinessProfile(checkedReadinessProfile);
44238
44238
  const readinessContract = systemInterface.readinessContract;
44239
44239
  if (!isBuiltIn && readinessContract === void 0) {
@@ -44241,13 +44241,13 @@ function computeInterfaceReadiness(model, request) {
44241
44241
  issues,
44242
44242
  "SYSTEM_INTERFACE_INVALID",
44243
44243
  "missing-readiness-contract",
44244
- `System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" uses custom readiness profile "${checkedReadinessProfile}" but declares no readinessContract. Built-in profiles: ${formatSupportedReadinessProfiles()}.`,
44245
- { path: `${readinessMarkerPath(request)}.readinessContract`, ref: checkedReadinessProfile }
44244
+ `System Interface "${formatInterfaceIdentity(request2.systemPath, request2.interfaceKey)}" uses custom readiness profile "${checkedReadinessProfile}" but declares no readinessContract. Built-in profiles: ${formatSupportedReadinessProfiles()}.`,
44245
+ { path: `${readinessMarkerPath(request2)}.readinessContract`, ref: checkedReadinessProfile }
44246
44246
  );
44247
44247
  return {
44248
44248
  ready: false,
44249
- systemPath: request.systemPath,
44250
- interfaceKey: request.interfaceKey,
44249
+ systemPath: request2.systemPath,
44250
+ interfaceKey: request2.interfaceKey,
44251
44251
  readinessProfile: checkedReadinessProfile,
44252
44252
  scopedResourceIds,
44253
44253
  issues
@@ -44258,29 +44258,29 @@ function computeInterfaceReadiness(model, request) {
44258
44258
  issues,
44259
44259
  "SYSTEM_INTERFACE_NOT_READY",
44260
44260
  "missing-scoped-resources",
44261
- `System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" must scope at least one active resource.`,
44262
- { path: `${readinessMarkerPath(request)}.resourceIds` }
44261
+ `System Interface "${formatInterfaceIdentity(request2.systemPath, request2.interfaceKey)}" must scope at least one active resource.`,
44262
+ { path: `${readinessMarkerPath(request2)}.resourceIds` }
44263
44263
  );
44264
44264
  }
44265
- const resources = getActiveScopedResources(model, scopedResourceIds, issues, request);
44265
+ const resources = getActiveScopedResources(model, scopedResourceIds, issues, request2);
44266
44266
  const index = tryCompileBusinessOntology(model, checkedReadinessProfile, issues);
44267
44267
  if (index !== void 0) {
44268
44268
  if (checkedReadinessProfile === LEAD_GEN_API_INTERFACE.readinessProfile) {
44269
- requireLeadGenInterfaceReadiness(issues, index, resources, request);
44269
+ requireLeadGenInterfaceReadiness(issues, index, resources, request2);
44270
44270
  } else if (checkedReadinessProfile === CRM_API_INTERFACE.readinessProfile) {
44271
- requireCrmInterfaceReadiness(issues, index, resources, request);
44271
+ requireCrmInterfaceReadiness(issues, index, resources, request2);
44272
44272
  } else if (checkedReadinessProfile === LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile) {
44273
- requireLeadGenInterfaceReadiness(issues, index, resources, request);
44274
- requireCrmInterfaceReadiness(issues, index, resources, { ...request, allowForeignOwner: true });
44275
- requireHandoffBridgeReadiness(issues, model, request);
44273
+ requireLeadGenInterfaceReadiness(issues, index, resources, request2);
44274
+ requireCrmInterfaceReadiness(issues, index, resources, { ...request2, allowForeignOwner: true });
44275
+ requireHandoffBridgeReadiness(issues, model, request2);
44276
44276
  } else if (readinessContract !== void 0) {
44277
- requireContractReadiness(issues, index, resources, readinessContract, request);
44277
+ requireContractReadiness(issues, index, resources, readinessContract, request2);
44278
44278
  }
44279
44279
  }
44280
44280
  return {
44281
44281
  ready: issues.length === 0,
44282
- systemPath: request.systemPath,
44283
- interfaceKey: request.interfaceKey,
44282
+ systemPath: request2.systemPath,
44283
+ interfaceKey: request2.interfaceKey,
44284
44284
  readinessProfile: checkedReadinessProfile,
44285
44285
  scopedResourceIds,
44286
44286
  issues
@@ -46160,22 +46160,72 @@ function getApiKey(apiUrl) {
46160
46160
  const key = resolveApiKey(isProd);
46161
46161
  if (!key) {
46162
46162
  const varName = isProd ? "ELEVASIS_PLATFORM_KEY" : "ELEVASIS_PLATFORM_KEY_DEV";
46163
- throw new Error(
46164
- `${varName} environment variable is required.
46165
- Set it in your .env file: ${varName}=sk_...`
46166
- );
46163
+ throw new Error(`${varName} environment variable is required.
46164
+ Set it in your .env file: ${varName}=sk_...`);
46167
46165
  }
46168
46166
  return key;
46169
46167
  }
46170
- async function apiGet(endpoint, apiUrl = resolveApiUrl()) {
46171
- const response = await fetch(`${apiUrl}${endpoint}`, {
46172
- headers: { Authorization: `Bearer ${getApiKey(apiUrl)}` }
46173
- });
46168
+ var DEFAULT_TIMEOUT_MS = 2 * 60 * 60 * 1e3;
46169
+ function resolveTimeoutMs() {
46170
+ const raw = process.env.ELEVASIS_CLI_TIMEOUT_MS;
46171
+ if (!raw) return DEFAULT_TIMEOUT_MS;
46172
+ const parsed = Number(raw);
46173
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS;
46174
+ }
46175
+ function formatElapsed(ms) {
46176
+ return ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
46177
+ }
46178
+ var GATEWAY_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
46179
+ function describeGatewayFailure(status, endpoint, elapsedMs) {
46180
+ return `API request failed (${status}) after ${formatElapsed(elapsedMs)}: a gateway between this CLI and the API ended the connection.
46181
+ This is NOT the API rejecting the request, and the work may still be running server-side -- a status code from a proxy says nothing about whether the handler completed.
46182
+ Endpoint: ${endpoint}
46183
+ Check for a result before re-running, or the same work may execute twice:
46184
+ executions -> 'elevasis execution <workflow> <id>'
46185
+ session turns -> 'elevasis-sdk session:messages <session-id> --all'
46186
+ Workflow executions can avoid this entirely with 'exec --async', which returns immediately
46187
+ and polls. Session turns have no async variant, so a long turn is exposed to this cutoff.`;
46188
+ }
46189
+ async function request(method, endpoint, apiUrl, body) {
46190
+ const timeoutMs = resolveTimeoutMs();
46191
+ const startedAt = Date.now();
46192
+ const headers = { Authorization: `Bearer ${getApiKey(apiUrl)}` };
46193
+ if (body !== void 0) headers["Content-Type"] = "application/json";
46194
+ let response;
46195
+ try {
46196
+ response = await fetch(`${apiUrl}${endpoint}`, {
46197
+ method,
46198
+ headers,
46199
+ ...body !== void 0 && { body: JSON.stringify(body) },
46200
+ signal: AbortSignal.timeout(timeoutMs)
46201
+ });
46202
+ } catch (error46) {
46203
+ const elapsedMs = Date.now() - startedAt;
46204
+ if (error46 instanceof Error && error46.name === "TimeoutError") {
46205
+ throw new Error(
46206
+ `Request timed out after ${formatElapsed(elapsedMs)} (limit ${formatElapsed(timeoutMs)}).
46207
+ Endpoint: ${endpoint}
46208
+ The server may still be working -- check for a result before re-running.
46209
+ Raise the limit with ELEVASIS_CLI_TIMEOUT_MS, or, for a workflow execution, submit it
46210
+ with 'exec --async'. Session turns have no async variant.`
46211
+ );
46212
+ }
46213
+ const detail = error46 instanceof Error ? error46.message : String(error46);
46214
+ throw new Error(
46215
+ `Could not reach the API after ${formatElapsed(elapsedMs)}: ${detail}
46216
+ Endpoint: ${apiUrl}${endpoint}
46217
+ No HTTP status was returned, so the request may never have been received.`
46218
+ );
46219
+ }
46174
46220
  if (!response.ok) {
46221
+ const elapsedMs = Date.now() - startedAt;
46175
46222
  const errorText = await response.text();
46176
46223
  if (response.status === 401) {
46177
46224
  throw new Error(`401 Unauthorized: Invalid or missing ELEVASIS_PLATFORM_KEY. Check your .env file.`);
46178
46225
  }
46226
+ if (GATEWAY_STATUSES.has(response.status)) {
46227
+ throw new Error(describeGatewayFailure(response.status, endpoint, elapsedMs));
46228
+ }
46179
46229
  throw new Error(`API request failed (${response.status}): ${errorText}`);
46180
46230
  }
46181
46231
  if (response.status === 204) {
@@ -46183,55 +46233,17 @@ async function apiGet(endpoint, apiUrl = resolveApiUrl()) {
46183
46233
  }
46184
46234
  return response.json();
46185
46235
  }
46236
+ async function apiGet(endpoint, apiUrl = resolveApiUrl()) {
46237
+ return request("GET", endpoint, apiUrl);
46238
+ }
46186
46239
  async function apiPost(endpoint, body, apiUrl = resolveApiUrl()) {
46187
- const response = await fetch(`${apiUrl}${endpoint}`, {
46188
- method: "POST",
46189
- headers: {
46190
- Authorization: `Bearer ${getApiKey(apiUrl)}`,
46191
- "Content-Type": "application/json"
46192
- },
46193
- body: JSON.stringify(body)
46194
- });
46195
- if (!response.ok) {
46196
- const errorText = await response.text();
46197
- throw new Error(`API request failed (${response.status}): ${errorText}`);
46198
- }
46199
- if (response.status === 204) {
46200
- return {};
46201
- }
46202
- return response.json();
46240
+ return request("POST", endpoint, apiUrl, body);
46203
46241
  }
46204
46242
  async function apiPatch(endpoint, body, apiUrl = resolveApiUrl()) {
46205
- const response = await fetch(`${apiUrl}${endpoint}`, {
46206
- method: "PATCH",
46207
- headers: {
46208
- Authorization: `Bearer ${getApiKey(apiUrl)}`,
46209
- "Content-Type": "application/json"
46210
- },
46211
- body: JSON.stringify(body)
46212
- });
46213
- if (!response.ok) {
46214
- const errorText = await response.text();
46215
- throw new Error(`API request failed (${response.status}): ${errorText}`);
46216
- }
46217
- if (response.status === 204) {
46218
- return {};
46219
- }
46220
- return response.json();
46243
+ return request("PATCH", endpoint, apiUrl, body);
46221
46244
  }
46222
46245
  async function apiDelete(endpoint, apiUrl = resolveApiUrl()) {
46223
- const response = await fetch(`${apiUrl}${endpoint}`, {
46224
- method: "DELETE",
46225
- headers: { Authorization: `Bearer ${getApiKey(apiUrl)}` }
46226
- });
46227
- if (!response.ok) {
46228
- const errorText = await response.text();
46229
- throw new Error(`API request failed (${response.status}): ${errorText}`);
46230
- }
46231
- if (response.status === 204) {
46232
- return {};
46233
- }
46234
- return response.json();
46246
+ return request("DELETE", endpoint, apiUrl);
46235
46247
  }
46236
46248
 
46237
46249
  // src/cli/wrap-action.ts
@@ -46253,7 +46265,7 @@ function wrapAction(commandName, fn) {
46253
46265
  // package.json
46254
46266
  var package_default = {
46255
46267
  name: "@elevasis/sdk",
46256
- version: "1.44.0",
46268
+ version: "1.44.2",
46257
46269
  description: "SDK for building Elevasis organization resources",
46258
46270
  type: "module",
46259
46271
  bin: {
@@ -5295,6 +5295,10 @@ var MEMORY_DOMAINS = {
5295
5295
  *
5296
5296
  * Tools manage complex structured data (caches, indexes, etc.)
5297
5297
  * that would be error-prone for LLM to update via JSON strings.
5298
+ *
5299
+ * No tool currently writes these three keys, so in practice the guard reserves the names
5300
+ * against the LLM rather than arbitrating a live conflict. That is still enforcement --
5301
+ * see `validateMemoryKeyOwnership` in `./utils` -- not dead code.
5298
5302
  */
5299
5303
  TOOL_OWNED: [
5300
5304
  "notion-pages-cache",
@@ -5303,20 +5307,10 @@ var MEMORY_DOMAINS = {
5303
5307
  // GitHub tools manage repository structure
5304
5308
  "slack-channel-cache"
5305
5309
  // Slack tools manage channel data
5306
- ],
5307
- /**
5308
- * Action-owned keys
5309
- * Updated by framework actions
5310
- * LLM cannot modify these via memoryOps
5311
- *
5312
- * Actions manage framework state that controls execution flow. Empty today -- the one
5313
- * action that ever wrote here was retired. Kept as its own domain because a future
5314
- * action-managed key belongs here, not folded into TOOL_OWNED.
5315
- */
5316
- ACTION_OWNED: []
5310
+ ]
5317
5311
  /**
5318
5312
  * LLM-owned keys
5319
- * All keys NOT in TOOL_OWNED or ACTION_OWNED
5313
+ * All keys NOT in TOOL_OWNED
5320
5314
  * Managed via memoryOps by agent reasoning
5321
5315
  *
5322
5316
  * LLM manages semantic memory (business context, decisions, etc.)
@@ -5326,12 +5320,8 @@ var MEMORY_DOMAINS = {
5326
5320
  function isToolOwnedKey(key) {
5327
5321
  return MEMORY_DOMAINS.TOOL_OWNED.includes(key);
5328
5322
  }
5329
- function isActionOwnedKey(key) {
5330
- return MEMORY_DOMAINS.ACTION_OWNED.includes(key);
5331
- }
5332
5323
  function getKeyOwner(key) {
5333
5324
  if (isToolOwnedKey(key)) return "tool";
5334
- if (isActionOwnedKey(key)) return "action";
5335
5325
  return "llm";
5336
5326
  }
5337
5327
 
@@ -5358,7 +5348,7 @@ function addToolError(memoryManager, action, errorMessage, iteration, turnNumber
5358
5348
  });
5359
5349
  }
5360
5350
  function validateMemoryKeyOwnership(key, logger, iteration) {
5361
- if (isToolOwnedKey(key) || isActionOwnedKey(key)) {
5351
+ if (isToolOwnedKey(key)) {
5362
5352
  const owner = getKeyOwner(key);
5363
5353
  logger.action(
5364
5354
  "memory-set-rejected",
@@ -5442,10 +5432,15 @@ var DEFAULT_EXECUTION_TIMEOUT = 72e5;
5442
5432
 
5443
5433
  // ../core/src/execution/engine/agent/memory/truncation.ts
5444
5434
  var CLOSING_BRACKET_RESERVE = 32;
5445
- function stripDanglingTail(text) {
5435
+ var DANGLING_KEY_WITH_COLON = /,?\s*"(?:[^"\\]|\\.)*"\s*:\s*$/;
5436
+ var DANGLING_KEY_NO_COLON = /([{,])\s*"(?:[^"\\]|\\.)*"\s*$/;
5437
+ function stripDanglingTail(text, innermostIsObject) {
5446
5438
  let out = text.replace(/,\s*$/, "");
5447
- const danglingKey = /,?\s*"(?:[^"\\]|\\.)*"\s*:\s*$/;
5448
- if (danglingKey.test(out)) out = out.replace(danglingKey, "").replace(/,\s*$/, "");
5439
+ if (DANGLING_KEY_WITH_COLON.test(out)) {
5440
+ out = out.replace(DANGLING_KEY_WITH_COLON, "").replace(/,\s*$/, "");
5441
+ } else if (innermostIsObject && DANGLING_KEY_NO_COLON.test(out)) {
5442
+ out = out.replace(DANGLING_KEY_NO_COLON, "$1").replace(/,\s*$/, "");
5443
+ }
5449
5444
  return out;
5450
5445
  }
5451
5446
  function safeStructuralPrefix(raw, cutAt) {
@@ -5472,7 +5467,7 @@ function safeStructuralPrefix(raw, cutAt) {
5472
5467
  }
5473
5468
  }
5474
5469
  const cutPoint = inString ? openStringStart : limit;
5475
- const base = stripDanglingTail(raw.slice(0, cutPoint));
5470
+ const base = stripDanglingTail(raw.slice(0, cutPoint), stack[stack.length - 1] === "}");
5476
5471
  return base + [...stack].reverse().join("");
5477
5472
  }
5478
5473
  function truncateContent(content, maxTokens) {
@@ -3387,6 +3387,10 @@ var MEMORY_DOMAINS = {
3387
3387
  *
3388
3388
  * Tools manage complex structured data (caches, indexes, etc.)
3389
3389
  * that would be error-prone for LLM to update via JSON strings.
3390
+ *
3391
+ * No tool currently writes these three keys, so in practice the guard reserves the names
3392
+ * against the LLM rather than arbitrating a live conflict. That is still enforcement --
3393
+ * see `validateMemoryKeyOwnership` in `./utils` -- not dead code.
3390
3394
  */
3391
3395
  TOOL_OWNED: [
3392
3396
  "notion-pages-cache",
@@ -3395,20 +3399,10 @@ var MEMORY_DOMAINS = {
3395
3399
  // GitHub tools manage repository structure
3396
3400
  "slack-channel-cache"
3397
3401
  // Slack tools manage channel data
3398
- ],
3399
- /**
3400
- * Action-owned keys
3401
- * Updated by framework actions
3402
- * LLM cannot modify these via memoryOps
3403
- *
3404
- * Actions manage framework state that controls execution flow. Empty today -- the one
3405
- * action that ever wrote here was retired. Kept as its own domain because a future
3406
- * action-managed key belongs here, not folded into TOOL_OWNED.
3407
- */
3408
- ACTION_OWNED: []
3402
+ ]
3409
3403
  /**
3410
3404
  * LLM-owned keys
3411
- * All keys NOT in TOOL_OWNED or ACTION_OWNED
3405
+ * All keys NOT in TOOL_OWNED
3412
3406
  * Managed via memoryOps by agent reasoning
3413
3407
  *
3414
3408
  * LLM manages semantic memory (business context, decisions, etc.)
@@ -3418,12 +3412,8 @@ var MEMORY_DOMAINS = {
3418
3412
  function isToolOwnedKey(key) {
3419
3413
  return MEMORY_DOMAINS.TOOL_OWNED.includes(key);
3420
3414
  }
3421
- function isActionOwnedKey(key) {
3422
- return MEMORY_DOMAINS.ACTION_OWNED.includes(key);
3423
- }
3424
3415
  function getKeyOwner(key) {
3425
3416
  if (isToolOwnedKey(key)) return "tool";
3426
- if (isActionOwnedKey(key)) return "action";
3427
3417
  return "llm";
3428
3418
  }
3429
3419
 
@@ -3450,7 +3440,7 @@ function addToolError(memoryManager, action, errorMessage, iteration, turnNumber
3450
3440
  });
3451
3441
  }
3452
3442
  function validateMemoryKeyOwnership(key, logger, iteration) {
3453
- if (isToolOwnedKey(key) || isActionOwnedKey(key)) {
3443
+ if (isToolOwnedKey(key)) {
3454
3444
  const owner = getKeyOwner(key);
3455
3445
  logger.action(
3456
3446
  "memory-set-rejected",
@@ -3534,10 +3524,15 @@ var DEFAULT_EXECUTION_TIMEOUT = 72e5;
3534
3524
 
3535
3525
  // ../core/src/execution/engine/agent/memory/truncation.ts
3536
3526
  var CLOSING_BRACKET_RESERVE = 32;
3537
- function stripDanglingTail(text) {
3527
+ var DANGLING_KEY_WITH_COLON = /,?\s*"(?:[^"\\]|\\.)*"\s*:\s*$/;
3528
+ var DANGLING_KEY_NO_COLON = /([{,])\s*"(?:[^"\\]|\\.)*"\s*$/;
3529
+ function stripDanglingTail(text, innermostIsObject) {
3538
3530
  let out = text.replace(/,\s*$/, "");
3539
- const danglingKey = /,?\s*"(?:[^"\\]|\\.)*"\s*:\s*$/;
3540
- if (danglingKey.test(out)) out = out.replace(danglingKey, "").replace(/,\s*$/, "");
3531
+ if (DANGLING_KEY_WITH_COLON.test(out)) {
3532
+ out = out.replace(DANGLING_KEY_WITH_COLON, "").replace(/,\s*$/, "");
3533
+ } else if (innermostIsObject && DANGLING_KEY_NO_COLON.test(out)) {
3534
+ out = out.replace(DANGLING_KEY_NO_COLON, "$1").replace(/,\s*$/, "");
3535
+ }
3541
3536
  return out;
3542
3537
  }
3543
3538
  function safeStructuralPrefix(raw, cutAt) {
@@ -3564,7 +3559,7 @@ function safeStructuralPrefix(raw, cutAt) {
3564
3559
  }
3565
3560
  }
3566
3561
  const cutPoint = inString ? openStringStart : limit;
3567
- const base = stripDanglingTail(raw.slice(0, cutPoint));
3562
+ const base = stripDanglingTail(raw.slice(0, cutPoint), stack[stack.length - 1] === "}");
3568
3563
  return base + [...stack].reverse().join("");
3569
3564
  }
3570
3565
  function truncateContent(content, maxTokens) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/sdk",
3
- "version": "1.44.0",
3
+ "version": "1.44.2",
4
4
  "description": "SDK for building Elevasis organization resources",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,9 +58,9 @@
58
58
  "tsup": "^8.0.0",
59
59
  "typescript": "5.9.2",
60
60
  "zod": "^4.1.0",
61
- "@repo/core": "0.58.0",
62
- "@repo/eslint-config": "0.0.0",
63
- "@repo/typescript-config": "0.0.0"
61
+ "@repo/core": "0.59.0",
62
+ "@repo/typescript-config": "0.0.0",
63
+ "@repo/eslint-config": "0.0.0"
64
64
  },
65
65
  "scripts": {
66
66
  "lint": "eslint src --max-warnings 0",
@@ -0,0 +1,122 @@
1
+ # Your auth guards now wait for organization context, and truncated tool results stay valid JSON
2
+
3
+ ## Why this note exists
4
+
5
+ This train publishes all three packages at once — `@elevasis/core`, `@elevasis/ui`, and
6
+ `@elevasis/sdk`. Four of the changes are things you can observe without changing a line of your own
7
+ code, which is exactly why they are worth reading before you take the baseline bump.
8
+
9
+ **1. `ProtectedRoute` now waits for organization context by default.** This is the one behavior
10
+ change most likely to surprise you, because nothing in your project asked for it.
11
+
12
+ Until now the default value of `waitForOrganization` depended on which import path a route file
13
+ happened to use. Routes importing the guard from `@elevasis/ui/features/auth` defaulted to `false`
14
+ and rendered before organization context existed; routes importing from `@elevasis/ui/auth`
15
+ defaulted to `true` and waited. One application, two guard behaviors, decided by an import
16
+ specifier. Both defaults are now `true`, resolved through a single shared predicate.
17
+
18
+ We audited every template-family project before shipping this rather than assuming it was safe.
19
+ Between 8 and 9 routes per project are affected — `index.tsx`, `knowledge.tsx`, and every
20
+ `settings/*.tsx` — and **not one route in any project passes the prop explicitly**. Every affected
21
+ route is organization-scoped, so none of them has a legitimate reason to render before organization
22
+ context arrives. The measurable cost is that `settings/appearance.tsx` and `settings/account.tsx`
23
+ are user-scoped and will now wait marginally longer than they strictly need to. That is latency, not
24
+ breakage.
25
+
26
+ If you have a route that genuinely must render before organization context exists, pass
27
+ `waitForOrganization={false}` explicitly. Relying on the old default is no longer possible.
28
+
29
+ **2. Invited members who have not signed up yet are now visibly distinguished.** A previous train
30
+ made them _appear_ in your members list, which fixed wrong counts. It did not make them
31
+ _identifiable_ — the type shipped but the renderer did not, so every member looked equally
32
+ provisioned. `ProvisioningStateBadge` now ships inside `@elevasis/ui` and `OrgMembersList` renders it
33
+ beside the existing status badge, in the same cell rather than a new column. A fully-provisioned
34
+ member renders nothing, so nothing about your existing rows changes.
35
+
36
+ **3. Membership responses carry an explicit `workosMembershipId`, and `membership.id` is the platform
37
+ UUID.** The API used to be inconsistent about which identifier a membership response carried. It is
38
+ now uniform: `id` is always the platform's own UUID, and the WorkOS identifier has its own named
39
+ field. Both forms are accepted on lookup, so a request built either way still resolves.
40
+
41
+ **`organization.workos_org_id` is unchanged and still carries the WorkOS `org_…` value verbatim.**
42
+ That is the field your project selects organizations on and drives `switchToOrganization` with. It
43
+ was audited specifically because this change was in the same area; nothing about it moved.
44
+
45
+ **4. Four invitation API routes were removed** — `POST /api/invitations/accept`, and the
46
+ `GET /api/invitations`, `GET /api/invitations/:id`, `GET /api/invitations/summary` trio. All four
47
+ were dead surface with no caller in any project we could find. If you have an out-of-tree script
48
+ calling one, it will now 404.
49
+
50
+ **5. `@elevasis/sdk` fixes a JSON-safety defect in truncated tool results.** When a long tool result
51
+ was truncated, a cut landing between a key's closing quote and its colon left behind a key with no
52
+ value — `{"alpha":"…","nested"}` — which is not parseable JSON. Your agents read slightly malformed
53
+ JSON rather than crashing, because the parser falls back to the raw string, so the practical impact
54
+ was low. It is fixed regardless. The window was a few characters per key, which is why casual testing
55
+ never surfaced it.
56
+
57
+ ## Applies to
58
+
59
+ - **Every template-family project**, for items 1 through 3. All are consumed through the published
60
+ packages with no source edits on your side.
61
+ - **Any project whose settings page renders `OrgMembersList`** — that is the published component,
62
+ so the badge arrives with the `@elevasis/ui` bump.
63
+ - **Every project running agents**, for item 5. The truncation fix lives in the worker runtime that
64
+ `operations/` bundles, so it reaches you through `@elevasis/sdk`.
65
+ - **Not applicable to your organization model, knowledge nodes, or workflow definitions.** Nothing in
66
+ this train touches authored content.
67
+
68
+ ## Required actions
69
+
70
+ 1. **Take the `@elevasis/core`, `@elevasis/ui`, and `@elevasis/sdk` baseline bumps** this train
71
+ propagates, then reinstall in `core/`, `ui/`, and `operations/`.
72
+
73
+ 2. **Clear the Vite cache and restart your dev server.** A stale `.vite` cache serves the old bundle
74
+ while every gate reports green — that is the specific way this failure hides:
75
+
76
+ ```bash
77
+ rm -rf ui/node_modules/.vite
78
+ pnpm -C ui dev
79
+ ```
80
+
81
+ 3. **Redeploy `operations/` to pick up the truncation fix.** The baseline bump changes what your
82
+ next bundle contains; it does not change what is already deployed:
83
+
84
+ ```bash
85
+ pnpm -C operations exec elevasis-sdk deploy
86
+ ```
87
+
88
+ 4. **Check any route that must render before organization context exists.** If you have one, add
89
+ `waitForOrganization={false}` explicitly. If you are unsure whether you have one, the audit result
90
+ above says you almost certainly do not — every affected route in every project is
91
+ organization-scoped already.
92
+
93
+ ## Verification
94
+
95
+ - **Sign in and land on your home route.** This is the check that matters most, because the guard
96
+ default changed on the route that greets you. Confirm it renders, and that navigating to
97
+ `settings/appearance` and `settings/account` still works — those are the two user-scoped pages that
98
+ now wait for organization context they do not strictly need.
99
+ - **Open your settings members page.** Any member you have invited who has not completed signup
100
+ should now carry a visible badge; fully-provisioned members should look exactly as before.
101
+ - **Read the installed bundle, not the version number.** A bumped pin and a green sync report are
102
+ claims about intent; the installed file is the only ground truth:
103
+
104
+ ```bash
105
+ grep -rl "ProvisioningStateBadge" ui/node_modules/@elevasis/ui/dist/components/
106
+ grep -c "innermostIsObject" operations/node_modules/@elevasis/sdk/dist/worker/index.js
107
+ ```
108
+
109
+ No match on the first, or `0` on the second, means the install did not land regardless of what
110
+ `package.json` says.
111
+
112
+ ## Not handled by /git-sync
113
+
114
+ - **The Vite cache clear and dev-server restart.** `/git-sync` propagates and commits the dependency
115
+ baseline. Your running dev server keeps serving the previously cached bundle until you do step 2
116
+ yourself, and it will look like the sync did nothing.
117
+ - **The `operations/` redeploy.** Bumping the `@elevasis/sdk` pin changes what your next bundle
118
+ contains. Your currently deployed workers keep running the old bundle until you deploy.
119
+ - **Adding `waitForOrganization={false}` to a route that needs it.** Nothing can detect that
120
+ automatically — only you know whether a route is legitimately pre-organization.
121
+ - **Replacing calls to the four removed invitation routes.** If an out-of-tree script calls one, the
122
+ sync cannot see it.
@@ -0,0 +1,120 @@
1
+ # Your CLI now explains a 502 instead of just reporting one, and no request hangs forever
2
+
3
+ ## Why this note exists
4
+
5
+ This train publishes one package — `@elevasis/sdk` — and every change in it is in the CLI you type
6
+ commands into. Nothing about your workflows, organization model, or UI moves. But the CLI's failure
7
+ messages change, and one of them changes in a way that is meant to stop you doing something harmful.
8
+
9
+ **1. A gateway failure now tells you the work may still be running, and how to check.** Until now
10
+ every non-2xx response produced the same shape: `API request failed (502)`. That message is
11
+ technically accurate and practically dangerous, because the most natural reaction to it is to run the
12
+ command again.
13
+
14
+ A 502, 503, or 504 does not come from the platform API. It comes from a hop in front of it that ended
15
+ your connection while the API was still working. The request you sent may have completed in full. We
16
+ found this the hard way: a session turn returned 502 to the CLI after 300 seconds, and a later
17
+ database query showed the turn had **completed successfully server-side in 319.8 seconds** with a
18
+ model reply written and no error of any kind. The only thing that failed was the connection carrying
19
+ the answer back. It was turn 14 of a 14-turn run — re-running blind would have executed that turn a
20
+ second time.
21
+
22
+ The new message says the proxy ended the connection, that this is not the API rejecting your request,
23
+ that the work may still be running, and which command to use to look for a result before re-running.
24
+
25
+ **2. Every CLI request is now bounded by a timeout, defaulting to 2 hours.** Before, the CLI used a
26
+ bare `fetch()` with no signal, so a connection that stalled would sit there indefinitely with no
27
+ output. The limit is deliberately set high — it matches the server's own socket budget on long routes.
28
+ The goal is to bound an indefinite hang, not to police how long your work may take. Anything shorter
29
+ would make the CLI give up on executions the API is still legitimately serving, turning a working long
30
+ run into a reported failure. Override it with `ELEVASIS_CLI_TIMEOUT_MS` if you have a reason to.
31
+
32
+ **3. "Timed out" and "never reached the API" are now different messages.** A bare `fetch()` rejection
33
+ carries no status, so a dead port, a wrong API URL, and a stalled connection were indistinguishable.
34
+ They now read differently.
35
+
36
+ **4. The friendly authentication message now applies to every verb.** A 401 on `GET` printed a clear
37
+ "check your platform key" message; a 401 on `POST`, `PATCH`, or `DELETE` printed the raw response
38
+ body. All four now share the same message.
39
+
40
+ **5. Three dead exports were removed from the agent memory surface.** `MEMORY_DOMAINS.ACTION_OWNED`
41
+ was an empty array, `isActionOwnedKey` returned `false` for every input, and the `ActionOwnedKey` type
42
+ resolved to `never`. They are gone. The live half of that layer is unchanged and still enforced: an
43
+ agent still cannot write to a tool-owned memory key. If your project imports any of the three removed
44
+ names, it was importing something that could never do anything — but the import will now fail, so it
45
+ is worth a grep.
46
+
47
+ ## Applies to
48
+
49
+ - **Every project that uses the `elevasis-sdk` CLI**, which is every template-family project. Items 1
50
+ through 4 arrive with the baseline bump and need no source edit from you.
51
+ - **Any script or CI job that parses CLI stderr.** The gateway, timeout, and transport messages are
52
+ new text. If something greps for `API request failed`, check it.
53
+ - **Any project importing `ACTION_OWNED`, `isActionOwnedKey`, or `ActionOwnedKey`** from the SDK, for
54
+ item 5. We found no such import in any template-family project, but only you can see out-of-tree
55
+ code.
56
+ - **Not applicable to your organization model, knowledge nodes, workflows, or UI.** Nothing in this
57
+ train touches authored content or any rendered surface.
58
+
59
+ ## Required actions
60
+
61
+ 1. **Take the `@elevasis/sdk` baseline bump** this train propagates, then reinstall in `operations/`:
62
+
63
+ ```bash
64
+ pnpm -C operations install
65
+ ```
66
+
67
+ 2. **Grep for the three removed memory exports** before you deploy. This is the only change in the
68
+ train that can break a build, and it is cheap to rule out:
69
+
70
+ ```bash
71
+ grep -rn "ACTION_OWNED\|isActionOwnedKey\|ActionOwnedKey" operations/src core/config
72
+ ```
73
+
74
+ No output means item 5 does not affect you.
75
+
76
+ 3. **Redeploy `operations/`.** The baseline bump changes what your next bundle contains; it does not
77
+ change what is already deployed:
78
+
79
+ ```bash
80
+ pnpm -C operations exec elevasis-sdk deploy
81
+ ```
82
+
83
+ 4. **Check any script that matches on CLI error text.** If you have automation that branches on
84
+ `API request failed`, it will no longer match a gateway failure.
85
+
86
+ ## Verification
87
+
88
+ - **Read the installed bundle, not the version number.** A bumped pin and a green sync report are
89
+ claims about intent; the installed file is the only ground truth:
90
+
91
+ ```bash
92
+ grep -c "GATEWAY_STATUSES" operations/node_modules/@elevasis/sdk/dist/cli.cjs
93
+ grep -c "ACTION_OWNED" operations/node_modules/@elevasis/sdk/dist/worker/index.js
94
+ ```
95
+
96
+ The first must be non-zero and the second must be `0`. Either result the other way means the
97
+ install did not land, regardless of what `package.json` says.
98
+
99
+ - **Provoke a transport failure and read the message.** The cheapest honest check that the new
100
+ branches are live, because it needs no broken server:
101
+
102
+ ```bash
103
+ ELEVASIS_API_URL=http://localhost:9 pnpm -C operations exec elevasis-sdk list
104
+ ```
105
+
106
+ You should get a message naming the endpoint and saying the request never reached the API — not a
107
+ bare `fetch failed`.
108
+
109
+ - **Confirm the timeout is configurable.** Setting `ELEVASIS_CLI_TIMEOUT_MS=1` on any command should
110
+ produce a timeout message that quotes the limit back to you.
111
+
112
+ ## Not handled by /git-sync
113
+
114
+ - **The `operations/` reinstall.** `/git-sync` propagates and commits the dependency baseline. The
115
+ `node_modules` copy your CLI actually executes is not updated until you run `pnpm install` yourself,
116
+ and the CLI will keep printing the old messages until you do.
117
+ - **The `operations/` redeploy.** Bumping the `@elevasis/sdk` pin changes what your next bundle
118
+ contains. Your currently deployed workers keep running the old bundle until you deploy.
119
+ - **The grep for the removed memory exports.** Nothing can detect an out-of-tree import for you.
120
+ - **Updating scripts that match on CLI error text.** The sync cannot see your automation.