lua-cli 3.36.0 → 3.38.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.js CHANGED
@@ -642,7 +642,6 @@ var init_constants = __esm({
642
642
  API: process.env.LUA_API_URL || "https://api.heylua.ai",
643
643
  AUTH: process.env.LUA_AUTH_URL || "https://auth.heylua.ai",
644
644
  CHAT: process.env.LUA_API_URL || "https://api.heylua.ai",
645
- WEBHOOK: process.env.LUA_WEBHOOK_URL || "https://webhook.heylua.ai",
646
645
  CDN: "https://cdn.heylua.ai"
647
646
  };
648
647
  CREDENTIALS_FILE = join(CLI_CONFIG_DIR, "credentials");
@@ -841,6 +840,12 @@ var init_package_root = __esm({
841
840
  import { z } from "zod";
842
841
  import { z as z2 } from "zod";
843
842
  import { z as z3 } from "zod";
843
+ function isChannelConversationChannel(channel) {
844
+ return !!channel && CHANNEL_CONVERSATION_CHANNELS.includes(channel);
845
+ }
846
+ function isChannelConversationThread(threadId, channel) {
847
+ return !!threadId && isChannelConversationChannel(channel);
848
+ }
844
849
  function isPersonaTextObject(value3) {
845
850
  if (value3 === null || typeof value3 !== "object" || Array.isArray(value3)) return false;
846
851
  const obj = value3;
@@ -1403,6 +1408,101 @@ function buildDefaultPersona(agentName) {
1403
1408
  function buildPersonalSpaceStartingPersona(agentName) {
1404
1409
  return PERSONAL_SPACE_STARTING_PERSONA.replace(new RegExp(AGENT_NAME_TOKEN.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), () => agentName || "Your assistant");
1405
1410
  }
1411
+ function maxDrainsForTier(tier) {
1412
+ return tier === "enterprise" ? LOG_DRAIN_MAX_PER_ORG_ENTERPRISE : LOG_DRAIN_MAX_PER_ORG;
1413
+ }
1414
+ function resolvePresetEndpoint(type, site) {
1415
+ const template3 = LOG_DRAIN_ENDPOINT_TEMPLATES[type];
1416
+ if (!template3 || !site) return void 0;
1417
+ return template3.replace("{site}", site);
1418
+ }
1419
+ function nextLogDrainQuotaResetAt(now) {
1420
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1)).toISOString();
1421
+ }
1422
+ function logDrainQuotaWindowExpired(usage, now) {
1423
+ const resetsAt = Date.parse(usage?.resetsAt ?? "");
1424
+ if (!Number.isFinite(resetsAt)) return true;
1425
+ return now.getTime() >= resetsAt;
1426
+ }
1427
+ function logDrainQuotaRatio(usage, now) {
1428
+ if (!usage) return 0;
1429
+ if (now && logDrainQuotaWindowExpired(usage, now)) return 0;
1430
+ const dimension = /* @__PURE__ */ __name2((used, limit) => {
1431
+ if (!Number.isFinite(limit) || limit <= 0) return 0;
1432
+ if (!Number.isFinite(used) || used <= 0) return 0;
1433
+ return used / limit;
1434
+ }, "dimension");
1435
+ return Math.max(dimension(usage.usedEvents, usage.eventsPerDay), dimension(usage.usedBytes, usage.bytesPerDay));
1436
+ }
1437
+ function logDrainQuotaDegradationForRatio(ratio) {
1438
+ if (!Number.isFinite(ratio)) return "none";
1439
+ if (ratio >= LOG_DRAIN_QUOTA_PAUSE_RATIO) return "paused";
1440
+ if (ratio >= LOG_DRAIN_QUOTA_DROP_INFO_RATIO) return "info_dropped";
1441
+ if (ratio >= LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO) return "debug_dropped";
1442
+ return "none";
1443
+ }
1444
+ function logDrainQuotaDegradation(usage, now) {
1445
+ return logDrainQuotaDegradationForRatio(logDrainQuotaRatio(usage, now));
1446
+ }
1447
+ function logDrainQuotaWarned(usage, now) {
1448
+ return logDrainQuotaRatio(usage, now) >= LOG_DRAIN_QUOTA_WARN_RATIO;
1449
+ }
1450
+ function logDrainQuotaDroppedSeverities(degradation) {
1451
+ switch (degradation) {
1452
+ case "debug_dropped":
1453
+ return [
1454
+ 5
1455
+ ];
1456
+ case "info_dropped":
1457
+ return [
1458
+ 5,
1459
+ 9
1460
+ ];
1461
+ default:
1462
+ return [];
1463
+ }
1464
+ }
1465
+ function encodeLogCursor(c) {
1466
+ return Buffer.from(JSON.stringify({
1467
+ t: c.t,
1468
+ id: c.id
1469
+ }), "utf8").toString("base64url");
1470
+ }
1471
+ function decodeLogCursor(raw) {
1472
+ if (typeof raw !== "string") return null;
1473
+ const trimmed2 = raw.trim();
1474
+ if (!trimmed2 || trimmed2.length > LOG_CURSOR_MAX_LENGTH) return null;
1475
+ let parsed;
1476
+ try {
1477
+ parsed = JSON.parse(Buffer.from(trimmed2, "base64url").toString("utf8"));
1478
+ } catch {
1479
+ return null;
1480
+ }
1481
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
1482
+ const { t, id } = parsed;
1483
+ if (typeof t !== "number" || !Number.isSafeInteger(t) || t < 0) return null;
1484
+ if (typeof id !== "string" || !id || id.length > LOG_CURSOR_MAX_ID_LENGTH) return null;
1485
+ return {
1486
+ t,
1487
+ id
1488
+ };
1489
+ }
1490
+ function parseLogTimeBound(raw, now = /* @__PURE__ */ new Date()) {
1491
+ if (typeof raw !== "string") return null;
1492
+ const value3 = raw.trim();
1493
+ if (!value3) return null;
1494
+ const relative3 = LOG_RELATIVE_TIME_PATTERN.exec(value3);
1495
+ if (relative3) {
1496
+ const amount = Number(relative3[1]);
1497
+ const unitMs = RELATIVE_UNIT_MS[relative3[2]];
1498
+ if (!Number.isFinite(amount) || unitMs === void 0) return null;
1499
+ const at2 = now.getTime() - amount * unitMs;
1500
+ return Number.isFinite(at2) ? new Date(at2) : null;
1501
+ }
1502
+ if (!LOG_ISO_TIME_PATTERN.test(value3)) return null;
1503
+ const at = Date.parse(value3);
1504
+ return Number.isNaN(at) ? null : new Date(at);
1505
+ }
1406
1506
  function isRecord(value3) {
1407
1507
  return typeof value3 === "object" && value3 !== null;
1408
1508
  }
@@ -1767,6 +1867,9 @@ function workflowOccurrenceId(lineageId, stepId) {
1767
1867
  function workflowOrgSlotKey(orgId, kind) {
1768
1868
  return `wf:org:${orgId}:${kind}`;
1769
1869
  }
1870
+ function workflowJobModelSourceLabel(source) {
1871
+ return WORKFLOW_JOB_MODEL_SOURCE_LABELS[source];
1872
+ }
1770
1873
  function scheduledTimeKey(scheduledTime) {
1771
1874
  return scheduledTime.replace(/[^0-9TZ]/g, "");
1772
1875
  }
@@ -1970,17 +2073,17 @@ function isWorkflowApprovalOutput(value3) {
1970
2073
  return typeof v.approved === "boolean" && WORKFLOW_APPROVAL_OUTPUT_DECISIONS.includes(v.decision) && typeof v.text === "string";
1971
2074
  }
1972
2075
  function extractSingleJsonValue(text) {
1973
- const trimmed = (text ?? "").trim();
1974
- if (!trimmed) return {
2076
+ const trimmed2 = (text ?? "").trim();
2077
+ if (!trimmed2) return {
1975
2078
  reason: "reply is empty"
1976
2079
  };
1977
2080
  const fenced = [
1978
- ...trimmed.matchAll(JSON_FENCE_RE)
2081
+ ...trimmed2.matchAll(JSON_FENCE_RE)
1979
2082
  ];
1980
2083
  if (fenced.length > 1) return {
1981
2084
  reason: "reply carries more than one fenced block"
1982
2085
  };
1983
- const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed;
2086
+ const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed2;
1984
2087
  try {
1985
2088
  return {
1986
2089
  value: JSON.parse(candidate)
@@ -1991,17 +2094,17 @@ function extractSingleJsonValue(text) {
1991
2094
  };
1992
2095
  }
1993
2096
  const opens = [
1994
- trimmed.indexOf("{"),
1995
- trimmed.indexOf("[")
2097
+ trimmed2.indexOf("{"),
2098
+ trimmed2.indexOf("[")
1996
2099
  ].filter((i) => i !== -1);
1997
2100
  const start = opens.length ? Math.min(...opens) : -1;
1998
- const end = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
2101
+ const end = Math.max(trimmed2.lastIndexOf("}"), trimmed2.lastIndexOf("]"));
1999
2102
  if (start === -1 || end <= start) return {
2000
2103
  reason: "reply is not JSON"
2001
2104
  };
2002
2105
  try {
2003
2106
  return {
2004
- value: JSON.parse(trimmed.slice(start, end + 1))
2107
+ value: JSON.parse(trimmed2.slice(start, end + 1))
2005
2108
  };
2006
2109
  } catch {
2007
2110
  return {
@@ -2077,12 +2180,39 @@ function effectiveAgentFeatures(base, override, rule) {
2077
2180
  if (rule === "wholesale") return override || base || void 0;
2078
2181
  return effectiveAgentFeatureRows(base, override).rows;
2079
2182
  }
2080
- var __defProp2, __name2, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_STANDING_START_TOOLS, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_AGENT_EFFORTS, WORKFLOW_MODEL_CLASSES, WORKFLOW_TASK_CLASSES, WORKFLOW_MODEL_TRAITS, WORKFLOW_MODEL_REASON_MAX_LENGTH, WORKFLOW_MODELS_COMPOSE_MODES, WORKFLOW_MODELS_PIN_MODES, WORKFLOW_MODELS_FALLBACK_MODES, WORKFLOW_MODELS_CONSENT_ACTIONS_MIN, WORKFLOW_DEFAULT_TASK_CLASS_MAP, WORKFLOW_MODELS_MAX_CLASS_DEFAULT, WORKFLOW_AUTONOMY_FORMS, WORKFLOW_AUTONOMY_NUMERIC_KEYS, WORKFLOW_AUTONOMY_ENVELOPE_KEYS, WORKFLOW_AUTONOMY_ENABLED_DEFAULT, WORKFLOW_AUTONOMY_DEFAULTS, WORKFLOW_AUTONOMY_CEILINGS, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_ENV, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_CEILING, WORKFLOW_AUTONOMY_FORMS_DEFAULT, WORKFLOW_AUTONOMY_MIN_VALUE, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, MODEL_ID_BYOK_PROVIDERS, MODEL_CLASS_REF_PREFIX, MODEL_SNAPSHOT_SUFFIX, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, AGENT_LOG_SOURCES, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, EventType, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, KEYS, MAX_BODY_BYTES, MAX_ID_LENGTH, UTC_ISO, TEMPLATE_TRIGGER_URL_ENV_PREFIX, TEMPLATE_INSTALL_POLICY_PER_WORKSPACE_VALUES, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, TYPED_API_KEY, IdSchema, SESSION_AUTH_TIME_MAX_S, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, LUA_SESSION_ID_CLAIM, SESSION_REVOKED_CODE, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_RUN_GATE_KINDS, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, WORKFLOW_SIGNAL_EVENT_SITES, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_SCHEDULE_KEY_MAX, WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_JOURNAL_PROTOCOL_VERSION, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_SUSPEND_KINDS, WORKFLOW_RUN_NOTIFICATION_SUSPENDED_KINDS, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_GOAL_JUDGE_SELF, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE, DEFAULT_ON_AGENT_FEATURES, SUBAGENT_PER_KEY_FLAG_ENV, PER_KEY_FLAG_VALUES;
2183
+ var __defProp2, __name2, PLUMBING_THREAD_CHANNELS, CHANNEL_CONVERSATION_CHANNELS, HIDDEN_THREAD_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_STANDING_START_TOOLS, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_AGENT_EFFORTS, WORKFLOW_MODEL_CLASSES, WORKFLOW_TASK_CLASSES, WORKFLOW_MODEL_TRAITS, WORKFLOW_MODEL_REASON_MAX_LENGTH, WORKFLOW_MODELS_COMPOSE_MODES, WORKFLOW_MODELS_PIN_MODES, WORKFLOW_MODELS_FALLBACK_MODES, WORKFLOW_MODELS_CONSENT_ACTIONS_MIN, WORKFLOW_DEFAULT_TASK_CLASS_MAP, WORKFLOW_MODELS_MAX_CLASS_DEFAULT, WORKFLOW_AUTONOMY_FORMS, WORKFLOW_AUTONOMY_NUMERIC_KEYS, WORKFLOW_AUTONOMY_ENVELOPE_KEYS, WORKFLOW_AUTONOMY_ENABLED_DEFAULT, WORKFLOW_AUTONOMY_DEFAULTS, WORKFLOW_AUTONOMY_CEILINGS, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_ENV, WORKFLOW_AUTONOMY_MAX_RUNS_PER_HOUR_CEILING, WORKFLOW_AUTONOMY_FORMS_DEFAULT, WORKFLOW_AUTONOMY_MIN_VALUE, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, MODEL_ID_BYOK_PROVIDERS, MODEL_CLASS_REF_PREFIX, MODEL_SNAPSHOT_SUFFIX, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, AGENT_LOG_SOURCES, AGENT_LOG_ENVIRONMENTS, LOG_DRAIN_TYPES_PHASE1, LOG_DRAIN_FORMATS, LOG_DRAIN_SEVERITIES, LOG_DRAIN_SOURCES, LOG_DRAIN_CONTENT_SOURCES, LOG_DRAIN_DEFAULT_SOURCES, LOG_DRAIN_MAX_PER_ORG, LOG_DRAIN_MAX_PER_ORG_ENTERPRISE, LOG_DRAIN_DATADOG_SITES, LOG_DRAIN_ENDPOINT_TEMPLATES, LOG_DRAIN_QUOTAS, LOG_DRAIN_QUOTA_WARN_RATIO, LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO, LOG_DRAIN_QUOTA_DROP_INFO_RATIO, LOG_DRAIN_QUOTA_PAUSE_RATIO, LOG_CURSOR_MAX_LENGTH, LOG_CURSOR_MAX_ID_LENGTH, LOG_RELATIVE_TIME_PATTERN, LOG_ISO_TIME_PATTERN, RELATIVE_UNIT_MS, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, EventType, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, KEYS, MAX_BODY_BYTES, MAX_ID_LENGTH, UTC_ISO, TEMPLATE_TRIGGER_URL_ENV_PREFIX, TEMPLATE_INSTALL_POLICY_PER_WORKSPACE_VALUES, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, TYPED_API_KEY, IdSchema, SESSION_AUTH_TIME_MAX_S, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, LUA_SESSION_ID_CLAIM, SESSION_REVOKED_CODE, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_RUN_GATE_KINDS, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, WORKFLOW_SIGNAL_EVENT_SITES, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_SCHEDULE_KEY_MAX, WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_JOURNAL_PROTOCOL_VERSION, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_JOB_MODEL_SOURCE_LABELS, WORKFLOW_SUSPEND_KINDS, WORKFLOW_RUN_NOTIFICATION_SUSPENDED_KINDS, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_GOAL_JUDGE_SELF, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE, DEFAULT_ON_AGENT_FEATURES, SUBAGENT_PER_KEY_FLAG_ENV, PER_KEY_FLAG_VALUES;
2081
2184
  var init_dist = __esm({
2082
2185
  "../shared-types/dist/index.mjs"() {
2083
2186
  "use strict";
2084
2187
  __defProp2 = Object.defineProperty;
2085
2188
  __name2 = /* @__PURE__ */ __name((target, value3) => __defProp2(target, "name", { value: value3, configurable: true }), "__name");
2189
+ PLUMBING_THREAD_CHANNELS = [
2190
+ "trigger",
2191
+ "template-install",
2192
+ "template-uninstall"
2193
+ ];
2194
+ CHANNEL_CONVERSATION_CHANNELS = [
2195
+ "whatsapp",
2196
+ "slack",
2197
+ "teams",
2198
+ "front",
2199
+ "email",
2200
+ "messagebird",
2201
+ "sms",
2202
+ "mms",
2203
+ "rcs",
2204
+ "instagram",
2205
+ "facebook",
2206
+ "imessage"
2207
+ ];
2208
+ HIDDEN_THREAD_CHANNELS = [
2209
+ ...PLUMBING_THREAD_CHANNELS,
2210
+ ...CHANNEL_CONVERSATION_CHANNELS
2211
+ ];
2212
+ __name(isChannelConversationChannel, "isChannelConversationChannel");
2213
+ __name2(isChannelConversationChannel, "isChannelConversationChannel");
2214
+ __name(isChannelConversationThread, "isChannelConversationThread");
2215
+ __name2(isChannelConversationThread, "isChannelConversationThread");
2086
2216
  __name(isPersonaTextObject, "isPersonaTextObject");
2087
2217
  __name2(isPersonaTextObject, "isPersonaTextObject");
2088
2218
  __name(flattenPersonaText, "flattenPersonaText");
@@ -2754,6 +2884,124 @@ This text is who you are for this person. As you learn them, their name, their w
2754
2884
  "workflow-script",
2755
2885
  "workflow"
2756
2886
  ];
2887
+ AGENT_LOG_ENVIRONMENTS = [
2888
+ "production",
2889
+ "sandbox"
2890
+ ];
2891
+ LOG_DRAIN_TYPES_PHASE1 = [
2892
+ "http",
2893
+ "otlp",
2894
+ "datadog",
2895
+ "betterstack"
2896
+ ];
2897
+ LOG_DRAIN_FORMATS = [
2898
+ "json",
2899
+ "ndjson"
2900
+ ];
2901
+ LOG_DRAIN_SEVERITIES = [
2902
+ "debug",
2903
+ "info",
2904
+ "warn",
2905
+ "error"
2906
+ ];
2907
+ LOG_DRAIN_SOURCES = [
2908
+ ...AGENT_LOG_SOURCES,
2909
+ "execution",
2910
+ // ── phase 2 ──
2911
+ "delivery",
2912
+ "trigger-execution",
2913
+ "job-execution",
2914
+ "workflow-run",
2915
+ "workflow-audit"
2916
+ ];
2917
+ LOG_DRAIN_CONTENT_SOURCES = [
2918
+ "user_message",
2919
+ "agent_response"
2920
+ ];
2921
+ LOG_DRAIN_DEFAULT_SOURCES = [
2922
+ "skill",
2923
+ "job",
2924
+ "webhook",
2925
+ "preprocessor",
2926
+ "postprocessor",
2927
+ "workflow-step",
2928
+ "workflow-script",
2929
+ "device",
2930
+ "runtime",
2931
+ "model-resolver",
2932
+ "trigger",
2933
+ "device-trigger",
2934
+ "workflow",
2935
+ "mcp",
2936
+ "rag",
2937
+ "execution",
2938
+ "agent_error"
2939
+ ];
2940
+ LOG_DRAIN_MAX_PER_ORG = 5;
2941
+ LOG_DRAIN_MAX_PER_ORG_ENTERPRISE = 10;
2942
+ __name(maxDrainsForTier, "maxDrainsForTier");
2943
+ __name2(maxDrainsForTier, "maxDrainsForTier");
2944
+ LOG_DRAIN_DATADOG_SITES = [
2945
+ "datadoghq.com",
2946
+ "datadoghq.eu",
2947
+ "us3.datadoghq.com",
2948
+ "us5.datadoghq.com",
2949
+ "ap1.datadoghq.com",
2950
+ "ddog-gov.com"
2951
+ ];
2952
+ LOG_DRAIN_ENDPOINT_TEMPLATES = {
2953
+ datadog: "https://http-intake.logs.{site}/api/v2/logs"
2954
+ };
2955
+ __name(resolvePresetEndpoint, "resolvePresetEndpoint");
2956
+ __name2(resolvePresetEndpoint, "resolvePresetEndpoint");
2957
+ LOG_DRAIN_QUOTAS = {
2958
+ team: {
2959
+ eventsPerDay: 1e6,
2960
+ bytesPerDay: 1 * 1024 ** 3
2961
+ },
2962
+ business: {
2963
+ eventsPerDay: 1e7,
2964
+ bytesPerDay: 10 * 1024 ** 3
2965
+ },
2966
+ enterprise: {
2967
+ eventsPerDay: Number.MAX_SAFE_INTEGER,
2968
+ bytesPerDay: Number.MAX_SAFE_INTEGER
2969
+ }
2970
+ };
2971
+ LOG_DRAIN_QUOTA_WARN_RATIO = 0.8;
2972
+ LOG_DRAIN_QUOTA_DROP_DEBUG_RATIO = 1;
2973
+ LOG_DRAIN_QUOTA_DROP_INFO_RATIO = 1.25;
2974
+ LOG_DRAIN_QUOTA_PAUSE_RATIO = 1.5;
2975
+ __name(nextLogDrainQuotaResetAt, "nextLogDrainQuotaResetAt");
2976
+ __name2(nextLogDrainQuotaResetAt, "nextLogDrainQuotaResetAt");
2977
+ __name(logDrainQuotaWindowExpired, "logDrainQuotaWindowExpired");
2978
+ __name2(logDrainQuotaWindowExpired, "logDrainQuotaWindowExpired");
2979
+ __name(logDrainQuotaRatio, "logDrainQuotaRatio");
2980
+ __name2(logDrainQuotaRatio, "logDrainQuotaRatio");
2981
+ __name(logDrainQuotaDegradationForRatio, "logDrainQuotaDegradationForRatio");
2982
+ __name2(logDrainQuotaDegradationForRatio, "logDrainQuotaDegradationForRatio");
2983
+ __name(logDrainQuotaDegradation, "logDrainQuotaDegradation");
2984
+ __name2(logDrainQuotaDegradation, "logDrainQuotaDegradation");
2985
+ __name(logDrainQuotaWarned, "logDrainQuotaWarned");
2986
+ __name2(logDrainQuotaWarned, "logDrainQuotaWarned");
2987
+ __name(logDrainQuotaDroppedSeverities, "logDrainQuotaDroppedSeverities");
2988
+ __name2(logDrainQuotaDroppedSeverities, "logDrainQuotaDroppedSeverities");
2989
+ LOG_CURSOR_MAX_LENGTH = 512;
2990
+ LOG_CURSOR_MAX_ID_LENGTH = 128;
2991
+ LOG_RELATIVE_TIME_PATTERN = /^(\d{1,9})([smhd])$/;
2992
+ LOG_ISO_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}([Tt ]\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?([Zz]|[+-]\d{2}:?\d{2})?)?$/;
2993
+ RELATIVE_UNIT_MS = {
2994
+ s: 1e3,
2995
+ m: 6e4,
2996
+ h: 36e5,
2997
+ d: 864e5
2998
+ };
2999
+ __name(encodeLogCursor, "encodeLogCursor");
3000
+ __name2(encodeLogCursor, "encodeLogCursor");
3001
+ __name(decodeLogCursor, "decodeLogCursor");
3002
+ __name2(decodeLogCursor, "decodeLogCursor");
3003
+ __name(parseLogTimeBound, "parseLogTimeBound");
3004
+ __name2(parseLogTimeBound, "parseLogTimeBound");
2757
3005
  CORE_DRAINING_CODE = "CORE_DRAINING";
2758
3006
  CORE_DRAINING_DEFAULT_RETRY_MS = 1e3;
2759
3007
  CORE_DRAINING_MAX_RETRY_MS = 5e3;
@@ -3520,6 +3768,16 @@ This text is who you are for this person. As you learn them, their name, their w
3520
3768
  __name2(workflowOrgSlotKey, "workflowOrgSlotKey");
3521
3769
  WORKFLOW_JOURNAL_PROTOCOL_VERSION = 1;
3522
3770
  WORKFLOW_CONNECTION_KEY_RE = /^[a-z][a-z0-9_-]{0,63}$/;
3771
+ WORKFLOW_JOB_MODEL_SOURCE_LABELS = {
3772
+ stamp: "lease stamp",
3773
+ initData: "run input",
3774
+ default: "placeholder default",
3775
+ node: "node.model",
3776
+ env: "platform default",
3777
+ org: "org default"
3778
+ };
3779
+ __name(workflowJobModelSourceLabel, "workflowJobModelSourceLabel");
3780
+ __name2(workflowJobModelSourceLabel, "workflowJobModelSourceLabel");
3523
3781
  __name(scheduledTimeKey, "scheduledTimeKey");
3524
3782
  __name2(scheduledTimeKey, "scheduledTimeKey");
3525
3783
  __name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
@@ -3753,7 +4011,23 @@ This text is who you are for this person. As you learn them, their name, their w
3753
4011
  * gated and nobody approved it, and conflating the two would make "who approved this run"
3754
4012
  * unanswerable for every run of either kind (§H2 "Audit").
3755
4013
  */
3756
- "workflow.run.autonomy_applied"
4014
+ "workflow.run.autonomy_applied",
4015
+ // --- run budget cap enforcement (Job-tier billing plan D4; BAC-677) ---
4016
+ /**
4017
+ * The audit twin of the `run.budget_exceeded` run event (WJB-B2a): the run overshot its
4018
+ * `budget.maxCredits` on its LAST step and completed anyway, `budget.exceeded: true`. The budget family
4019
+ * already mirrors run events here — `workflow.run.budget_parked` / `workflow.run.budget_raised` are the
4020
+ * twins of `run.budget_parked` / `run.budget_raised` — and an organisation reviewing what it was charged
4021
+ * needs the overshoot in the audit store beside the raise that preceded it, not only in the run's own
4022
+ * event stream.
4023
+ *
4024
+ * Declared with the run event rather than after it because the emitter port
4025
+ * (`WorkflowAuditPort.action`, typed `workflow.run.${string}`) does not check against this tuple: a name
4026
+ * that is NOT declared here is written to the audit store all the same and no compiler says so.
4027
+ * Declaring it is what makes the later emitter checkable. `workflow.run.budget_parked` is likewise
4028
+ * declared and not yet emitted.
4029
+ */
4030
+ "workflow.run.budget_exceeded"
3757
4031
  ];
3758
4032
  WORKFLOW_AUDIT_METADATA_MAX_BYTES = 4 * 1024;
3759
4033
  __name(isWorkflowAuditEvent, "isWorkflowAuditEvent");
@@ -4763,10 +5037,10 @@ function parseModelTemplate(model) {
4763
5037
  if (!looksLikeModelTemplate(model)) return {
4764
5038
  kind: "static"
4765
5039
  };
4766
- const trimmed = model.trim();
4767
- const m = WORKFLOW_MODEL_TEMPLATE_RE.exec(trimmed);
5040
+ const trimmed2 = model.trim();
5041
+ const m = WORKFLOW_MODEL_TEMPLATE_RE.exec(trimmed2);
4768
5042
  if (!m) {
4769
- const inner = /^\$\{([^}]*)\}$/.exec(trimmed)?.[1];
5043
+ const inner = /^\$\{([^}]*)\}$/.exec(trimmed2)?.[1];
4770
5044
  const root = inner?.split(/[.|]/, 1)[0];
4771
5045
  const why = inner !== void 0 && root !== void 0 && root !== WORKFLOW_MODEL_TEMPLATE_ROOT ? `only the \`${WORKFLOW_MODEL_TEMPLATE_ROOT}\` root is allowed (got \`${root || "(empty)"}\`)` : inner !== void 0 && (inner === WORKFLOW_MODEL_TEMPLATE_ROOT || inner.startsWith(`${WORKFLOW_MODEL_TEMPLATE_ROOT}.`)) ? "the path after `initData.` is empty" : "the whole value must be exactly one placeholder \u2014 no prefix, suffix or second placeholder";
4772
5046
  return {
@@ -4796,7 +5070,7 @@ function parseModelTemplate(model) {
4796
5070
  return {
4797
5071
  kind: "template",
4798
5072
  template: {
4799
- placeholder: trimmed,
5073
+ placeholder: trimmed2,
4800
5074
  path: path26,
4801
5075
  default: dflt
4802
5076
  }
@@ -4805,7 +5079,7 @@ function parseModelTemplate(model) {
4805
5079
  return {
4806
5080
  kind: "template",
4807
5081
  template: {
4808
- placeholder: trimmed,
5082
+ placeholder: trimmed2,
4809
5083
  path: path26
4810
5084
  }
4811
5085
  };
@@ -5458,9 +5732,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
5458
5732
  const registry = opts.approvedModels;
5459
5733
  if (registry === void 0) return;
5460
5734
  if (registry === "unavailable") {
5461
- const trimmed = pin.trim();
5462
- if (trimmed && !normalizeModelId(trimmed, []).ok) {
5463
- warn("model-unresolved", inContext(`model "${trimmed}" could not be checked against the approved-model registry (unavailable at push) \u2014 it dispatches only if it resolves there (a provider/model registry code, or a bare id exactly one approved model carries)`), `${path26}.model`, id);
5735
+ const trimmed2 = pin.trim();
5736
+ if (trimmed2 && !normalizeModelId(trimmed2, []).ok) {
5737
+ warn("model-unresolved", inContext(`model "${trimmed2}" could not be checked against the approved-model registry (unavailable at push) \u2014 it dispatches only if it resolves there (a provider/model registry code, or a bare id exactly one approved model carries)`), `${path26}.model`, id);
5464
5738
  }
5465
5739
  return;
5466
5740
  }
@@ -7992,6 +8266,49 @@ function graphHasJobTierStep(envelopeOrGraph) {
7992
8266
  const graph = Array.isArray(envelopeOrGraph) ? envelopeOrGraph : env.definition?.graph ?? [];
7993
8267
  return Array.isArray(graph) && graph.some(entryHasJobTier);
7994
8268
  }
8269
+ function jobTierSteps(envelopeOrGraph) {
8270
+ const out = [];
8271
+ if (!envelopeOrGraph || typeof envelopeOrGraph !== "object") return out;
8272
+ const env = envelopeOrGraph;
8273
+ const graph = Array.isArray(envelopeOrGraph) ? envelopeOrGraph : env.definition?.graph ?? [];
8274
+ if (!Array.isArray(graph)) return out;
8275
+ for (const entry of graph) collectJobTierSteps(entry, out);
8276
+ return out;
8277
+ }
8278
+ function jobTierStepIds(envelopeOrGraph) {
8279
+ return jobTierSteps(envelopeOrGraph).map((s) => s.stepId);
8280
+ }
8281
+ function collectJobTierSteps(entry, out) {
8282
+ const n2 = armEntry2(entry);
8283
+ if (!n2 || typeof n2 !== "object") return;
8284
+ const node = n2;
8285
+ const ws = node.workspace;
8286
+ if (node.tier === "job" || ws !== void 0 && ws !== "inherit") {
8287
+ const stepId = typeof node.id === "string" && node.id ? node.id : "(unnamed)";
8288
+ const model = typeof node.model === "string" && node.model.trim() ? node.model.trim() : void 0;
8289
+ out.push(model === void 0 ? {
8290
+ stepId
8291
+ } : {
8292
+ stepId,
8293
+ model
8294
+ });
8295
+ }
8296
+ switch (node.type) {
8297
+ case "parallel":
8298
+ if (Array.isArray(node.steps)) for (const s of node.steps) collectJobTierSteps(s, out);
8299
+ return;
8300
+ case "conditional":
8301
+ if (Array.isArray(node.steps)) for (const s of node.steps) collectJobTierSteps(s, out);
8302
+ if (node.otherwise !== void 0) collectJobTierSteps(node.otherwise, out);
8303
+ return;
8304
+ case "foreach":
8305
+ case "loop":
8306
+ collectJobTierSteps(node.step, out);
8307
+ return;
8308
+ default:
8309
+ return;
8310
+ }
8311
+ }
7995
8312
  var __defProp4, __name4, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, WORKFLOW_MODEL_TEMPLATE_ROOT, WORKFLOW_MODEL_TEMPLATE_RE, PATH_SEGMENT_RE, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, WORKFLOW_INLINE_RUN_TAG, RUN_ERROR_ISSUES_MAX, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, WORKFLOW_SCHEDULE_TYPES, WORKFLOW_SCHEDULE_SHAPE_ISSUE, WORKFLOW_SCHEDULE_SHAPES_HINT, WORKFLOW_SCHEDULE_RUN_AS, isObject, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
7996
8313
  var init_dist2 = __esm({
7997
8314
  "../workflow-graph/dist/index.mjs"() {
@@ -8796,6 +9113,12 @@ var init_dist2 = __esm({
8796
9113
  __name4(entryHasJobTier, "entryHasJobTier");
8797
9114
  __name(graphHasJobTierStep, "graphHasJobTierStep");
8798
9115
  __name4(graphHasJobTierStep, "graphHasJobTierStep");
9116
+ __name(jobTierSteps, "jobTierSteps");
9117
+ __name4(jobTierSteps, "jobTierSteps");
9118
+ __name(jobTierStepIds, "jobTierStepIds");
9119
+ __name4(jobTierStepIds, "jobTierStepIds");
9120
+ __name(collectJobTierSteps, "collectJobTierSteps");
9121
+ __name4(collectJobTierSteps, "collectJobTierSteps");
8799
9122
  }
8800
9123
  });
8801
9124
 
@@ -12376,8 +12699,8 @@ function ensureGitignored(rootDir, entry, afterEntry) {
12376
12699
  return;
12377
12700
  }
12378
12701
  }
12379
- const trimmed = content.endsWith("\n") ? content : content + "\n";
12380
- fs2.writeFileSync(gitignorePath, trimmed + entry + "\n");
12702
+ const trimmed2 = content.endsWith("\n") ? content : content + "\n";
12703
+ fs2.writeFileSync(gitignorePath, trimmed2 + entry + "\n");
12381
12704
  }
12382
12705
  function hasEnvFile() {
12383
12706
  return fs2.existsSync(path2.join(process.cwd(), ".env"));
@@ -27776,6 +28099,7 @@ var init_mcp_server_handler = __esm({
27776
28099
 
27777
28100
  // src/index.ts
27778
28101
  import { Command } from "commander";
28102
+ import chalk5 from "chalk";
27779
28103
 
27780
28104
  // src/cli/parse-agent-version.ts
27781
28105
  import { InvalidArgumentError } from "commander";
@@ -29658,8 +29982,8 @@ var PostProcessorApi = class extends HttpClient {
29658
29982
  // src/utils/sync-fetch.ts
29659
29983
  init_developer_api_service();
29660
29984
  init_mcp_server_handler();
29661
- function normalizeForComparison(str) {
29662
- return str.trim().replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
29985
+ function normalizeForComparison(str2) {
29986
+ return str2.trim().replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
29663
29987
  }
29664
29988
  __name(normalizeForComparison, "normalizeForComparison");
29665
29989
  function canonicalizeForm(value3) {
@@ -31504,12 +31828,30 @@ __name(buildLogsCommand, "buildLogsCommand");
31504
31828
  // src/utils/sync-display.ts
31505
31829
  init_dist();
31506
31830
  import { diffLines } from "diff";
31831
+
31832
+ // src/utils/color.ts
31833
+ function noColorEnabled(env = process.env) {
31834
+ return !!env.NO_COLOR;
31835
+ }
31836
+ __name(noColorEnabled, "noColorEnabled");
31837
+ function colorize(ansiCode, text) {
31838
+ if (noColorEnabled()) return text;
31839
+ return `${ansiCode}${text}\x1B[0m`;
31840
+ }
31841
+ __name(colorize, "colorize");
31842
+ function configureChalkColor(chalkLike) {
31843
+ if (noColorEnabled()) {
31844
+ chalkLike.level = 0;
31845
+ }
31846
+ }
31847
+ __name(configureChalkColor, "configureChalkColor");
31848
+
31849
+ // src/utils/sync-display.ts
31507
31850
  var colors = {
31508
- red: "\x1B[31m",
31509
- green: "\x1B[32m",
31510
- gray: "\x1B[90m",
31511
- reset: "\x1B[0m",
31512
- bold: "\x1B[1m"
31851
+ red: /* @__PURE__ */ __name((s) => colorize("\x1B[31m", s), "red"),
31852
+ green: /* @__PURE__ */ __name((s) => colorize("\x1B[32m", s), "green"),
31853
+ gray: /* @__PURE__ */ __name((s) => colorize("\x1B[90m", s), "gray"),
31854
+ bold: /* @__PURE__ */ __name((s) => colorize("\x1B[1m", s), "bold")
31513
31855
  };
31514
31856
  var primitiveTypeLabels = {
31515
31857
  skill: "SKILLS",
@@ -31521,31 +31863,31 @@ var primitiveTypeLabels = {
31521
31863
  };
31522
31864
  function showColoredDiff(oldText, newText, oldLabel = "Server", newLabel = "Local") {
31523
31865
  if (!oldText && !newText) {
31524
- console.log(colors.gray + "(both empty)" + colors.reset);
31866
+ console.log(colors.gray("(both empty)"));
31525
31867
  return;
31526
31868
  }
31527
31869
  if (!oldText) {
31528
- console.log(colors.green + `+ (${newLabel} only - no ${oldLabel} content)` + colors.reset);
31870
+ console.log(colors.green(`+ (${newLabel} only - no ${oldLabel} content)`));
31529
31871
  return;
31530
31872
  }
31531
31873
  if (!newText) {
31532
- console.log(colors.red + `- (${oldLabel} only - no ${newLabel} content)` + colors.reset);
31874
+ console.log(colors.red(`- (${oldLabel} only - no ${newLabel} content)`));
31533
31875
  return;
31534
31876
  }
31535
31877
  const diff = diffLines(oldText, newText);
31536
31878
  diff.forEach((part) => {
31537
31879
  if (part.added) {
31538
- part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.green + "+ " + line + colors.reset));
31880
+ part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.green("+ " + line)));
31539
31881
  } else if (part.removed) {
31540
- part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.red + "- " + line + colors.reset));
31882
+ part.value.split("\n").filter((l) => l).forEach((line) => console.log(colors.red("- " + line)));
31541
31883
  } else {
31542
31884
  const lines = part.value.split("\n").filter((l) => l);
31543
31885
  if (lines.length <= 6) {
31544
- lines.forEach((line) => console.log(colors.gray + " " + line + colors.reset));
31886
+ lines.forEach((line) => console.log(colors.gray(" " + line)));
31545
31887
  } else {
31546
- lines.slice(0, 3).forEach((line) => console.log(colors.gray + " " + line + colors.reset));
31547
- console.log(colors.gray + ` ... (${lines.length - 6} unchanged lines) ...` + colors.reset);
31548
- lines.slice(-3).forEach((line) => console.log(colors.gray + " " + line + colors.reset));
31888
+ lines.slice(0, 3).forEach((line) => console.log(colors.gray(" " + line)));
31889
+ console.log(colors.gray(` ... (${lines.length - 6} unchanged lines) ...`));
31890
+ lines.slice(-3).forEach((line) => console.log(colors.gray(" " + line)));
31549
31891
  }
31550
31892
  }
31551
31893
  });
@@ -31553,8 +31895,8 @@ function showColoredDiff(oldText, newText, oldLabel = "Server", newLabel = "Loca
31553
31895
  __name(showColoredDiff, "showColoredDiff");
31554
31896
  function showPersonaDiff(serverPersona, localPersona) {
31555
31897
  console.log("\n" + "=".repeat(60));
31556
- console.log(colors.bold + "PERSONA DIFF" + colors.reset);
31557
- console.log(colors.red + "- Server (deployed)" + colors.reset + " " + colors.green + "+ Local (code)" + colors.reset);
31898
+ console.log(colors.bold("PERSONA DIFF"));
31899
+ console.log(colors.red("- Server (deployed)") + " " + colors.green("+ Local (code)"));
31558
31900
  console.log("=".repeat(60) + "\n");
31559
31901
  showColoredDiff(flattenPersonaTextAll(serverPersona), flattenPersonaTextAll(localPersona), "Server", "Local");
31560
31902
  console.log("\n" + "=".repeat(60) + "\n");
@@ -31562,19 +31904,19 @@ function showPersonaDiff(serverPersona, localPersona) {
31562
31904
  __name(showPersonaDiff, "showPersonaDiff");
31563
31905
  function showNameDiff(serverName, localName) {
31564
31906
  console.log("\n" + "=".repeat(60));
31565
- console.log(colors.bold + "NAME DIFF" + colors.reset);
31907
+ console.log(colors.bold("NAME DIFF"));
31566
31908
  console.log("=".repeat(60));
31567
- console.log(colors.red + `- Server: "${serverName || "(none)"}"` + colors.reset);
31568
- console.log(colors.green + `+ Local: "${localName || "(none)"}"` + colors.reset);
31909
+ console.log(colors.red(`- Server: "${serverName || "(none)"}"`));
31910
+ console.log(colors.green(`+ Local: "${localName || "(none)"}"`));
31569
31911
  console.log("=".repeat(60) + "\n");
31570
31912
  }
31571
31913
  __name(showNameDiff, "showNameDiff");
31572
31914
  function showModelDiff(serverModel, localModel) {
31573
31915
  console.log("\n" + "=".repeat(60));
31574
- console.log(colors.bold + "MODEL DIFF" + colors.reset);
31916
+ console.log(colors.bold("MODEL DIFF"));
31575
31917
  console.log("=".repeat(60));
31576
- console.log(colors.red + `- Server: "${serverModel || "(none)"}"` + colors.reset);
31577
- console.log(colors.green + `+ Local: "${localModel || "(none)"}"` + colors.reset);
31918
+ console.log(colors.red(`- Server: "${serverModel || "(none)"}"`));
31919
+ console.log(colors.green(`+ Local: "${localModel || "(none)"}"`));
31578
31920
  console.log("=".repeat(60) + "\n");
31579
31921
  }
31580
31922
  __name(showModelDiff, "showModelDiff");
@@ -31593,17 +31935,17 @@ function showPrimitiveDrift(items, backupAvailable) {
31593
31935
  }
31594
31936
  for (const [type, groupItems] of grouped) {
31595
31937
  const label2 = primitiveTypeLabels[type] ?? type.toUpperCase();
31596
- console.log("\n" + colors.bold + `\u2501\u2501\u2501 ${label2} \u2501\u2501\u2501` + colors.reset);
31938
+ console.log("\n" + colors.bold(`\u2501\u2501\u2501 ${label2} \u2501\u2501\u2501`));
31597
31939
  for (const item of groupItems) {
31598
- console.log("\n " + colors.bold + item.name + colors.reset);
31599
- const localDisplay = item.localVersion ? `v${item.localVersion}` : colors.gray + "(not found)" + colors.reset;
31940
+ console.log("\n " + colors.bold(item.name));
31941
+ const localDisplay = item.localVersion ? `v${item.localVersion}` : colors.gray("(not found)");
31600
31942
  console.log(` Local: ${localDisplay}`);
31601
- const serverDisplay = item.serverVersion ? `v${item.serverVersion}` : colors.gray + "(not found)" + colors.reset;
31943
+ const serverDisplay = item.serverVersion ? `v${item.serverVersion}` : colors.gray("(not found)");
31602
31944
  console.log(` Server: ${serverDisplay}`);
31603
31945
  }
31604
31946
  }
31605
31947
  if (backupAvailable !== void 0) {
31606
- const backupDisplay = backupAvailable ? colors.green + "available" + colors.reset : colors.red + "not available" + colors.reset;
31948
+ const backupDisplay = backupAvailable ? colors.green("available") : colors.red("not available");
31607
31949
  console.log(`
31608
31950
  Backup: ${backupDisplay}`);
31609
31951
  }
@@ -31625,14 +31967,14 @@ function showPrimitiveDriftSummary(items) {
31625
31967
  versionInfo = `local only${vSuffix(item.localVersion)}`;
31626
31968
  }
31627
31969
  const paddedLabel = label2.padEnd(16);
31628
- console.log(` ${colors.red}${paddedLabel}${colors.reset} ${item.name} (${versionInfo})`);
31970
+ console.log(` ${colors.red(paddedLabel)} ${item.name} (${versionInfo})`);
31629
31971
  }
31630
31972
  }
31631
31973
  __name(showPrimitiveDriftSummary, "showPrimitiveDriftSummary");
31632
31974
  function showGovernanceDiff(serverGovernance, localGovernance) {
31633
31975
  console.log("\n" + "=".repeat(60));
31634
- console.log(colors.bold + "GOVERNANCE DIFF" + colors.reset);
31635
- console.log(colors.red + "- Server (deployed)" + colors.reset + " " + colors.green + "+ Local (code)" + colors.reset);
31976
+ console.log(colors.bold("GOVERNANCE DIFF"));
31977
+ console.log(colors.red("- Server (deployed)") + " " + colors.green("+ Local (code)"));
31636
31978
  console.log("=".repeat(60) + "\n");
31637
31979
  showColoredDiff(JSON.stringify(serverGovernance, null, 2) || "(none)", JSON.stringify(localGovernance, null, 2) || "(none)", "Server", "Local");
31638
31980
  console.log("\n" + "=".repeat(60) + "\n");
@@ -36930,6 +37272,9 @@ init_artifact_loader();
36930
37272
  init_types();
36931
37273
  init_analytics();
36932
37274
 
37275
+ // src/utils/aliases.ts
37276
+ init_dist();
37277
+
36933
37278
  // src/utils/suggest.ts
36934
37279
  function levenshtein(a, b) {
36935
37280
  const m = a.length, n2 = b.length;
@@ -37062,21 +37407,58 @@ var ALIAS_MAP = {
37062
37407
  everything: "all"
37063
37408
  })
37064
37409
  },
37410
+ // PRO-1798 (log drains) — `lua drains <action>`; the canonical list is §B.14's verb
37411
+ // table. `rotate-secret` keeps its hyphen: it is the route name too.
37412
+ "drains.action": {
37413
+ canonical: [
37414
+ "list",
37415
+ "status",
37416
+ "deliveries",
37417
+ "create",
37418
+ "update",
37419
+ "delete",
37420
+ "test",
37421
+ "verify",
37422
+ "pause",
37423
+ "resume",
37424
+ "rotate-secret"
37425
+ ],
37426
+ aliases: lowerKeys({
37427
+ ls: "list",
37428
+ view: "list",
37429
+ show: "status",
37430
+ info: "status",
37431
+ health: "status",
37432
+ history: "deliveries",
37433
+ attempts: "deliveries",
37434
+ deliver: "deliveries",
37435
+ new: "create",
37436
+ add: "create",
37437
+ edit: "update",
37438
+ patch: "update",
37439
+ rm: "delete",
37440
+ remove: "delete",
37441
+ del: "delete",
37442
+ send: "test",
37443
+ check: "verify",
37444
+ off: "pause",
37445
+ disable: "pause",
37446
+ on: "resume",
37447
+ enable: "resume",
37448
+ rotate: "rotate-secret",
37449
+ rotatesecret: "rotate-secret",
37450
+ "rotate-key": "rotate-secret"
37451
+ })
37452
+ },
37065
37453
  "logs.type": {
37454
+ // PRO-1838 (A5) / design D-17: DERIVED from the canonical constant, never hand-listed.
37455
+ // `logs.ts` already derives its own enum from `AGENT_LOG_SOURCES`, but THIS list is what
37456
+ // actually rejects `--type` — hand-maintaining it left the five platform sources
37457
+ // (trigger, model-resolver, workflow, workflow-step, workflow-script) unreachable.
37458
+ // `getCanonicalValues` sorts, so the order here is irrelevant to the `Valid values` hint.
37459
+ // `all` and `calls` are handled by the caller before the check and are not members.
37066
37460
  canonical: [
37067
- "skill",
37068
- "job",
37069
- "webhook",
37070
- "preprocessor",
37071
- "postprocessor",
37072
- "user_message",
37073
- "agent_response",
37074
- "agent_error",
37075
- "mcp",
37076
- "runtime",
37077
- "rag",
37078
- "device",
37079
- "device-trigger"
37461
+ ...AGENT_LOG_SOURCES
37080
37462
  ],
37081
37463
  aliases: lowerKeys({
37082
37464
  skills: "skill",
@@ -40275,8 +40657,8 @@ async function gitUserConfigured(cwd) {
40275
40657
  cwd
40276
40658
  });
40277
40659
  if (code !== 0) return void 0;
40278
- const trimmed = stdout.trim();
40279
- return trimmed.length > 0 ? trimmed : void 0;
40660
+ const trimmed2 = stdout.trim();
40661
+ return trimmed2.length > 0 ? trimmed2 : void 0;
40280
40662
  } catch {
40281
40663
  return void 0;
40282
40664
  }
@@ -40301,8 +40683,8 @@ async function getRemoteOriginUrl(cwd) {
40301
40683
  cwd
40302
40684
  });
40303
40685
  if (code !== 0) return null;
40304
- const trimmed = stdout.trim();
40305
- return trimmed.length > 0 ? trimmed : null;
40686
+ const trimmed2 = stdout.trim();
40687
+ return trimmed2.length > 0 ? trimmed2 : null;
40306
40688
  } catch {
40307
40689
  return null;
40308
40690
  }
@@ -40673,9 +41055,9 @@ async function readCurrentBranch(cwd) {
40673
41055
  cwd
40674
41056
  });
40675
41057
  if (res.code !== 0) return null;
40676
- const trimmed = res.stdout.trim();
40677
- if (!trimmed || trimmed === "HEAD") return null;
40678
- return trimmed;
41058
+ const trimmed2 = res.stdout.trim();
41059
+ if (!trimmed2 || trimmed2 === "HEAD") return null;
41060
+ return trimmed2;
40679
41061
  }
40680
41062
  __name(readCurrentBranch, "readCurrentBranch");
40681
41063
 
@@ -40793,8 +41175,8 @@ __name(reportFailure, "reportFailure");
40793
41175
  var GIT_MESSAGES = {
40794
41176
  pushStaged: "lua: push staged code",
40795
41177
  createVersion: /* @__PURE__ */ __name((version, message) => {
40796
- const trimmed = message?.trim();
40797
- return trimmed ? `lua: create version v${version}: ${trimmed}` : `lua: create version v${version}`;
41178
+ const trimmed2 = message?.trim();
41179
+ return trimmed2 ? `lua: create version v${version}: ${trimmed2}` : `lua: create version v${version}`;
40798
41180
  }, "createVersion"),
40799
41181
  promote: /* @__PURE__ */ __name((version) => `lua: promote v${version} to production`, "promote"),
40800
41182
  delete: /* @__PURE__ */ __name((version) => `lua: delete version v${version}`, "delete"),
@@ -43411,17 +43793,22 @@ var LogsApi = class extends HttpClient {
43411
43793
  }
43412
43794
  /**
43413
43795
  * Retrieves logs for an agent
43796
+ *
43797
+ * The signature is unchanged from before A4 — `filters` simply gained four optional
43798
+ * keys. When `filters.cursor` is set, `page` is NOT sent: the route refuses the two
43799
+ * together (docs `reference/rest/log-drains.mdx`, "Cannot be combined with `page`").
43800
+ *
43414
43801
  * @param agentId - The ID of the agent
43415
43802
  * @param limit - Number of logs per page (default: 20)
43416
- * @param page - Page number (default: 1)
43803
+ * @param page - Page number (default: 1); ignored when `filters.cursor` is set
43417
43804
  * @param filters - Optional filters to narrow log results
43418
- * @returns Promise resolving to an ApiResponse containing logs and pagination
43805
+ * @returns Promise resolving to an ApiResponse containing logs, nextCursor and pagination
43419
43806
  */
43420
43807
  async getAgentLogs(agentId, limit, page, filters = {}) {
43421
43808
  const queryParams = new URLSearchParams({
43422
- limit: limit.toString(),
43423
- page: page.toString()
43809
+ limit: limit.toString()
43424
43810
  });
43811
+ if (!filters.cursor) queryParams.append("page", page.toString());
43425
43812
  if (filters.logSource) queryParams.append("logSource", filters.logSource);
43426
43813
  if (filters.primitiveId) queryParams.append("primitiveId", filters.primitiveId);
43427
43814
  if (filters.primitiveName) queryParams.append("primitiveName", filters.primitiveName);
@@ -43429,6 +43816,10 @@ var LogsApi = class extends HttpClient {
43429
43816
  if (filters.toolName) queryParams.append("toolName", filters.toolName);
43430
43817
  if (filters.logType) queryParams.append("logType", filters.logType);
43431
43818
  if (filters.userId) queryParams.append("userId", filters.userId);
43819
+ if (filters.since) queryParams.append("since", filters.since);
43820
+ if (filters.until) queryParams.append("until", filters.until);
43821
+ if (filters.environment) queryParams.append("environment", filters.environment);
43822
+ if (filters.cursor) queryParams.append("cursor", filters.cursor);
43432
43823
  return this.httpGet(`/developer/agents/${agentId}/logs?${queryParams.toString()}`, {});
43433
43824
  }
43434
43825
  };
@@ -43729,14 +44120,14 @@ async function startChatLoop(chatEnv, probeWindow) {
43729
44120
  });
43730
44121
  rl.prompt();
43731
44122
  rl.on("line", async (input) => {
43732
- const trimmed = input.trim();
43733
- if (!trimmed) {
44123
+ const trimmed2 = input.trim();
44124
+ if (!trimmed2) {
43734
44125
  rl.prompt();
43735
44126
  return;
43736
44127
  }
43737
44128
  let messages;
43738
44129
  try {
43739
- messages = await parseMessageWithAttachments(trimmed);
44130
+ messages = await parseMessageWithAttachments(trimmed2);
43740
44131
  } catch (error) {
43741
44132
  console.error(`
43742
44133
  \u274C ${error instanceof Error ? error.message : "Failed to process attachments"}
@@ -46554,8 +46945,8 @@ async function promptAreaCode() {
46554
46945
  default: ""
46555
46946
  }
46556
46947
  ]);
46557
- const trimmed = pattern.trim();
46558
- return trimmed.length > 0 ? trimmed : void 0;
46948
+ const trimmed2 = pattern.trim();
46949
+ return trimmed2.length > 0 ? trimmed2 : void 0;
46559
46950
  }
46560
46951
  __name(promptAreaCode, "promptAreaCode");
46561
46952
  function smsLabel(transportOrUndefined) {
@@ -47500,6 +47891,7 @@ var CallMetricsApi = class extends HttpClient {
47500
47891
 
47501
47892
  // src/commands/logs.ts
47502
47893
  init_cli_error();
47894
+ init_dist();
47503
47895
  var ALL_VALID_SOURCES = AGENT_LOG_SOURCES;
47504
47896
  var ENTITY_SOURCES = /* @__PURE__ */ new Set([
47505
47897
  "skill",
@@ -47510,19 +47902,35 @@ var ENTITY_SOURCES = /* @__PURE__ */ new Set([
47510
47902
  "device"
47511
47903
  ]);
47512
47904
  var NON_ENTITY_SOURCES = new Set(ALL_VALID_SOURCES.filter((s) => !ENTITY_SOURCES.has(s)));
47905
+ var FOLLOW_POLL_MS = 2e3;
47906
+ var FOLLOW_POLL_JITTER_MS = 500;
47907
+ var FOLLOW_BACKOFF_MIN_MS = 1e3;
47908
+ var FOLLOW_BACKOFF_MAX_MS = 3e4;
47909
+ var FOLLOW_PAGE_CAP = 10;
47910
+ var RELATIVE_WINDOW = LOG_RELATIVE_TIME_PATTERN;
47911
+ var ISO_TIME_SHAPE = LOG_ISO_TIME_PATTERN;
47513
47912
  async function logsCommand(cmdObj) {
47514
47913
  return withErrorHandling(async () => {
47914
+ const pageGiven = cmdObj?.page !== void 0 && cmdObj?.page !== null && cmdObj?.page !== "";
47515
47915
  const options = {
47516
47916
  type: cmdObj?.type || null,
47517
47917
  name: cmdObj?.name || null,
47518
47918
  limit: cmdObj?.limit ? parseInt(cmdObj.limit, 10) : 20,
47519
- page: cmdObj?.page ? parseInt(cmdObj.page, 10) : 1,
47919
+ page: pageGiven ? parseInt(cmdObj.page, 10) : 1,
47920
+ pageGiven,
47520
47921
  json: cmdObj?.json || false,
47521
47922
  userId: cmdObj?.userId || null,
47522
47923
  agentId: cmdObj?.agentId || null,
47523
47924
  direction: cmdObj?.direction || null,
47524
- status: cmdObj?.status || null
47925
+ status: cmdObj?.status || null,
47926
+ since: cmdObj?.since ? parseWindowBound("--since", String(cmdObj.since)) : void 0,
47927
+ until: cmdObj?.until ? parseWindowBound("--until", String(cmdObj.until)) : void 0,
47928
+ follow: cmdObj?.follow || false,
47929
+ environment: cmdObj?.environment ? parseEnvironment(String(cmdObj.environment)) : void 0
47525
47930
  };
47931
+ if (options.follow && options.pageGiven) {
47932
+ throw CliError.usage("lua: --follow cannot be combined with --page.", "Drop --page: --follow starts from --since (or the newest page) and keeps going.");
47933
+ }
47526
47934
  const { agentId: configAgentId, apiKey } = await initializeCommand();
47527
47935
  const effectiveAgentId = options.agentId || configAgentId;
47528
47936
  if (options.type === "calls") {
@@ -47537,7 +47945,18 @@ async function logsCommand(cmdObj) {
47537
47945
  return;
47538
47946
  }
47539
47947
  const logsApi = new LogsApi(BASE_URLS.API, apiKey);
47540
- const hasNonInteractiveFlags = options.type || options.name || options.json || options.userId;
47948
+ const hasNonInteractiveFlags = Boolean(options.type || options.name || options.json || options.userId || options.since || options.until || options.environment || options.follow);
47949
+ trackEvent("cli_logs_action", {
47950
+ filter_type: options.type || null,
47951
+ json_output: options.json || false,
47952
+ non_interactive: hasNonInteractiveFlags,
47953
+ page_requested: options.page || 1,
47954
+ agent_id_override: !!options.agentId,
47955
+ follow: !!options.follow,
47956
+ has_since: !!options.since,
47957
+ has_until: !!options.until,
47958
+ environment: options.environment || null
47959
+ });
47541
47960
  if (hasNonInteractiveFlags) {
47542
47961
  await nonInteractiveLogs(logsApi, effectiveAgentId, apiKey, options);
47543
47962
  } else {
@@ -47545,13 +47964,6 @@ async function logsCommand(cmdObj) {
47545
47964
  `);
47546
47965
  await interactiveLogs(logsApi, effectiveAgentId, apiKey);
47547
47966
  }
47548
- trackEvent("cli_logs_action", {
47549
- filter_type: options.type || null,
47550
- json_output: options.json || false,
47551
- non_interactive: hasNonInteractiveFlags,
47552
- page_requested: options.page || 1,
47553
- agent_id_override: !!options.agentId
47554
- });
47555
47967
  }, "logs viewing");
47556
47968
  }
47557
47969
  __name(logsCommand, "logsCommand");
@@ -47603,7 +48015,7 @@ function renderLogEntry(log, isLast) {
47603
48015
  const logSource = log.metadata?.logSource;
47604
48016
  const primitiveId = log.metadata?.primitiveId;
47605
48017
  const primitiveName = log.metadata?.primitiveName;
47606
- const { toolName, channel, userId } = log.metadata;
48018
+ const { toolName, channel, userId, environment, agentVersion } = log.metadata;
47607
48019
  const icon = getLogIcon(log.subType, logSource);
47608
48020
  const color = getLogColor(log.subType, logSource);
47609
48021
  console.log(color(`${icon} [${timestamp}] ${log.subType.toUpperCase()}`));
@@ -47626,6 +48038,12 @@ function renderLogEntry(log, isLast) {
47626
48038
  console.log(chalk2.gray(` Tool Name: ${toolName}`));
47627
48039
  }
47628
48040
  }
48041
+ if (environment) {
48042
+ console.log(chalk2.gray(` Env: ${environment}`));
48043
+ }
48044
+ if (agentVersion) {
48045
+ console.log(chalk2.gray(` Version: ${agentVersion}`));
48046
+ }
47629
48047
  if (log.duration !== void 0) {
47630
48048
  console.log(chalk2.gray(` Duration: ${log.duration}ms`));
47631
48049
  }
@@ -47635,6 +48053,13 @@ function renderLogEntry(log, isLast) {
47635
48053
  }
47636
48054
  }
47637
48055
  __name(renderLogEntry, "renderLogEntry");
48056
+ function formatPageHeader(pagination) {
48057
+ if (pagination?.totalCountExact === false) {
48058
+ return `Page ${pagination.currentPage} (${pagination.totalCount}+ total logs \u2014 count capped; page with --json nextCursor)`;
48059
+ }
48060
+ return `Page ${pagination.currentPage} of ${pagination.totalPages} (${pagination.totalCount} total logs)`;
48061
+ }
48062
+ __name(formatPageHeader, "formatPageHeader");
47638
48063
  function displayLogsCore(logs, pagination, title, clearScreen = false) {
47639
48064
  if (clearScreen) {
47640
48065
  console.clear();
@@ -47643,7 +48068,7 @@ function displayLogsCore(logs, pagination, title, clearScreen = false) {
47643
48068
  ${title}
47644
48069
  `);
47645
48070
  writeInfo(`${"\u2500".repeat(80)}`);
47646
- writeInfo(`Page ${pagination.currentPage} of ${pagination.totalPages} (${pagination.totalCount} total logs)
48071
+ writeInfo(`${formatPageHeader(pagination)}
47647
48072
  `);
47648
48073
  if (logs.length === 0) {
47649
48074
  writeInfo("No logs found.");
@@ -47699,6 +48124,149 @@ async function resolveEntityId(apiKey, agentId, type, nameOrId) {
47699
48124
  return found?.id || null;
47700
48125
  }
47701
48126
  __name(resolveEntityId, "resolveEntityId");
48127
+ function parseWindowBound(flag, raw) {
48128
+ const value3 = raw.trim();
48129
+ if (RELATIVE_WINDOW.test(value3)) return value3;
48130
+ if (ISO_TIME_SHAPE.test(value3)) {
48131
+ const parsed = Date.parse(value3);
48132
+ if (Number.isFinite(parsed)) return new Date(parsed).toISOString();
48133
+ }
48134
+ throw CliError.usage(`lua: ${flag} "${raw}" is not a time.`, `Use an ISO 8601 instant (2026-09-21T09:00:00Z) or a relative window: 15m, 2h, 7d.`);
48135
+ }
48136
+ __name(parseWindowBound, "parseWindowBound");
48137
+ function parseEnvironment(raw) {
48138
+ const value3 = raw.trim().toLowerCase();
48139
+ const valid = [
48140
+ ...AGENT_LOG_ENVIRONMENTS
48141
+ ].sort();
48142
+ if (valid.includes(value3)) return value3;
48143
+ throw CliError.usage(`lua: "${raw}" is not a valid --environment value.`, `Valid values: ${valid.join(", ")}`);
48144
+ }
48145
+ __name(parseEnvironment, "parseEnvironment");
48146
+ function followDelayMs() {
48147
+ const raw = Number(process.env.LUA_LOGS_FOLLOW_INTERVAL_MS);
48148
+ const base = Number.isFinite(raw) && raw >= 0 && raw <= 6e4 ? raw : FOLLOW_POLL_MS;
48149
+ const jitter = Math.min(FOLLOW_POLL_JITTER_MS, Math.floor(base / 4));
48150
+ return base + (jitter > 0 ? Math.floor(Math.random() * jitter) : 0);
48151
+ }
48152
+ __name(followDelayMs, "followDelayMs");
48153
+ function sleep2(ms, signal) {
48154
+ if (signal.aborted) return Promise.resolve();
48155
+ return new Promise((resolve7) => {
48156
+ const done = /* @__PURE__ */ __name(() => {
48157
+ clearTimeout(timer);
48158
+ signal.removeEventListener("abort", done);
48159
+ resolve7();
48160
+ }, "done");
48161
+ const timer = setTimeout(done, ms);
48162
+ signal.addEventListener("abort", done, {
48163
+ once: true
48164
+ });
48165
+ });
48166
+ }
48167
+ __name(sleep2, "sleep");
48168
+ function rowKey(row2) {
48169
+ return row2.id || row2._id;
48170
+ }
48171
+ __name(rowKey, "rowKey");
48172
+ async function sweepWindow(logsApi, agentId, limit, filters, pageCap) {
48173
+ const rows3 = [];
48174
+ let cursor = filters.cursor;
48175
+ let pagination;
48176
+ let pages = 0;
48177
+ for (; ; ) {
48178
+ const response = await logsApi.getAgentLogs(agentId, limit, 1, {
48179
+ ...filters,
48180
+ cursor
48181
+ });
48182
+ if (!response.success) {
48183
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error", void 0, apiErrorDetail(response.error));
48184
+ }
48185
+ const data = response.data;
48186
+ rows3.push(...data.logs ?? []);
48187
+ pagination = data.pagination;
48188
+ pages += 1;
48189
+ const next = data.nextCursor ?? null;
48190
+ if (!next || pages >= pageCap) {
48191
+ return {
48192
+ rows: rows3,
48193
+ nextCursor: next,
48194
+ pagination,
48195
+ capped: Boolean(next) && pages >= pageCap
48196
+ };
48197
+ }
48198
+ cursor = next;
48199
+ }
48200
+ }
48201
+ __name(sweepWindow, "sweepWindow");
48202
+ async function followLogs(logsApi, agentId, options, baseFilters) {
48203
+ const limit = options.limit || 20;
48204
+ const controller = new AbortController();
48205
+ const onSigint = /* @__PURE__ */ __name(() => controller.abort(), "onSigint");
48206
+ process.once("SIGINT", onSigint);
48207
+ const untilMs = options.until ? RELATIVE_WINDOW.test(options.until) ? 0 : Date.parse(options.until) : void 0;
48208
+ let boundarySeen = /* @__PURE__ */ new Set();
48209
+ let since = baseFilters.since;
48210
+ let backoff = 0;
48211
+ try {
48212
+ while (!controller.signal.aborted) {
48213
+ let sweep;
48214
+ try {
48215
+ const pageCap = since ? FOLLOW_PAGE_CAP : 1;
48216
+ sweep = await sweepWindow(logsApi, agentId, limit, {
48217
+ ...baseFilters,
48218
+ since,
48219
+ cursor: void 0
48220
+ }, pageCap);
48221
+ } catch (error) {
48222
+ if (controller.signal.aborted) break;
48223
+ if (error instanceof CliError && error.exitCode === CLI_EXIT.UNAVAILABLE) {
48224
+ backoff = Math.min(FOLLOW_BACKOFF_MAX_MS, backoff ? backoff * 2 : FOLLOW_BACKOFF_MIN_MS);
48225
+ await sleep2(backoff, controller.signal);
48226
+ continue;
48227
+ }
48228
+ throw error;
48229
+ }
48230
+ backoff = 0;
48231
+ const fresh = sweep.rows.filter((row2) => !boundarySeen.has(rowKey(row2)));
48232
+ if (fresh.length > 0) {
48233
+ if (options.json) {
48234
+ console.log(JSON.stringify({
48235
+ logs: fresh,
48236
+ nextCursor: sweep.nextCursor ?? null,
48237
+ pagination: sweep.pagination ?? null
48238
+ }, null, 2));
48239
+ } else {
48240
+ const ordered = [
48241
+ ...fresh
48242
+ ].reverse();
48243
+ ordered.forEach((row2, index) => renderLogEntry(row2, index === ordered.length - 1));
48244
+ }
48245
+ if (sweep.capped && !options.json) {
48246
+ writeInfo(` \u2026 more rows remain in this window (stopped after ${FOLLOW_PAGE_CAP} pages); raising --limit catches up faster.`);
48247
+ }
48248
+ }
48249
+ if (sweep.rows.length > 0) {
48250
+ let newestMs = Number.NEGATIVE_INFINITY;
48251
+ for (const row2 of sweep.rows) {
48252
+ const ms = Date.parse(row2.timestamp);
48253
+ if (Number.isFinite(ms) && ms > newestMs) newestMs = ms;
48254
+ }
48255
+ if (Number.isFinite(newestMs)) {
48256
+ since = new Date(newestMs).toISOString();
48257
+ boundarySeen = new Set(sweep.rows.filter((row2) => Date.parse(row2.timestamp) === newestMs).map((row2) => rowKey(row2)));
48258
+ }
48259
+ }
48260
+ if (untilMs !== void 0 && Date.now() >= untilMs) break;
48261
+ if (controller.signal.aborted) break;
48262
+ await sleep2(followDelayMs(), controller.signal);
48263
+ }
48264
+ } finally {
48265
+ process.off("SIGINT", onSigint);
48266
+ if (!controller.signal.aborted) controller.abort();
48267
+ }
48268
+ }
48269
+ __name(followLogs, "followLogs");
47702
48270
  async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
47703
48271
  const filters = {};
47704
48272
  if (options.type && options.type !== "all") {
@@ -47721,6 +48289,13 @@ async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
47721
48289
  if (options.userId) {
47722
48290
  filters.userId = options.userId;
47723
48291
  }
48292
+ if (options.since) filters.since = options.since;
48293
+ if (options.until) filters.until = options.until;
48294
+ if (options.environment) filters.environment = options.environment;
48295
+ if (options.follow) {
48296
+ await followLogs(logsApi, agentId, options, filters);
48297
+ return;
48298
+ }
47724
48299
  const response = await logsApi.getAgentLogs(agentId, options.limit || 20, options.page || 1, filters);
47725
48300
  if (!response.success) {
47726
48301
  throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Unknown error", void 0, apiErrorDetail(response.error));
@@ -47729,6 +48304,10 @@ async function nonInteractiveLogs(logsApi, agentId, apiKey, options) {
47729
48304
  if (options.json) {
47730
48305
  console.log(JSON.stringify({
47731
48306
  logs: data.logs,
48307
+ // Always present so a script can `while (nextCursor)` without probing for the
48308
+ // key; `null` is the documented end-of-stream, and a server that predates A4
48309
+ // simply never sets it.
48310
+ nextCursor: data.nextCursor ?? null,
47732
48311
  pagination: data.pagination
47733
48312
  }, null, 2));
47734
48313
  } else {
@@ -48017,8 +48596,8 @@ async function handleNavigation(action, pagination, setPage) {
48017
48596
  }
48018
48597
  }
48019
48598
  __name(handleNavigation, "handleNavigation");
48020
- function capitalize(str) {
48021
- return str.charAt(0).toUpperCase() + str.slice(1);
48599
+ function capitalize(str2) {
48600
+ return str2.charAt(0).toUpperCase() + str2.slice(1);
48022
48601
  }
48023
48602
  __name(capitalize, "capitalize");
48024
48603
  function getLogIcon(subType, logSource) {
@@ -48082,6 +48661,1423 @@ function getLogColor(subType, logSource) {
48082
48661
  }
48083
48662
  __name(getLogColor, "getLogColor");
48084
48663
 
48664
+ // src/commands/drains.ts
48665
+ init_cli();
48666
+ init_command_utils();
48667
+ init_files();
48668
+ init_credential_operational_context();
48669
+ init_constants();
48670
+ init_cli_error();
48671
+ init_analytics();
48672
+
48673
+ // src/api/drains.api.service.ts
48674
+ init_http_client();
48675
+ var DrainsApi = class extends HttpClient {
48676
+ static {
48677
+ __name(this, "DrainsApi");
48678
+ }
48679
+ orgId;
48680
+ constructor(baseUrl, credential, orgId) {
48681
+ super(baseUrl, credential), this.orgId = orgId;
48682
+ }
48683
+ /**
48684
+ * The base path every call hangs off — RELATIVE to `baseUrl`, because every
48685
+ * `HttpClient` verb prepends `this.baseUrl` itself (`httpGet(url) => baseUrl + url`).
48686
+ * The scaffolded body interpolated `baseUrl` a second time, which would have sent
48687
+ * every request to `<base><base>/admin/…`; the signature is unchanged.
48688
+ */
48689
+ base() {
48690
+ return `/admin/orgs/${encodeURIComponent(this.orgId)}/log-drains`;
48691
+ }
48692
+ /** `…/log-drains/:drainId` with the id encoded. */
48693
+ drainPath(drainId, suffix = "") {
48694
+ return `${this.base()}/${encodeURIComponent(drainId)}${suffix}`;
48695
+ }
48696
+ async listDrains() {
48697
+ return this.httpGet(this.base());
48698
+ }
48699
+ async getDrain(drainId) {
48700
+ return this.httpGet(this.drainPath(drainId));
48701
+ }
48702
+ /** The response carries `secret` — print it ONCE and never store it. */
48703
+ async createDrain(input) {
48704
+ return this.httpPostOnce(this.base(), input);
48705
+ }
48706
+ async updateDrain(drainId, input) {
48707
+ return this.httpPatch(this.drainPath(drainId), input);
48708
+ }
48709
+ async deleteDrain(drainId) {
48710
+ return this.httpDelete(this.drainPath(drainId));
48711
+ }
48712
+ async testDrain(drainId) {
48713
+ return this.httpPost(this.drainPath(drainId, "/test"));
48714
+ }
48715
+ async verifyDrain(drainId) {
48716
+ return this.httpPost(this.drainPath(drainId, "/verify"));
48717
+ }
48718
+ async pauseDrain(drainId, reason) {
48719
+ return this.httpPost(this.drainPath(drainId, "/pause"), reason ? {
48720
+ reason
48721
+ } : {});
48722
+ }
48723
+ async resumeDrain(drainId) {
48724
+ return this.httpPost(this.drainPath(drainId, "/resume"), {});
48725
+ }
48726
+ /**
48727
+ * `finalize` closes an open rotation window instead of opening a new one — that
48728
+ * answer is a plain `LogDrainRead` and is safe to retry; minting is not.
48729
+ */
48730
+ async rotateSecret(drainId, finalize) {
48731
+ const path26 = this.drainPath(drainId, `/rotate-secret${finalize ? "?finalize=true" : ""}`);
48732
+ return finalize ? this.httpPost(path26, {}) : this.httpPostOnce(path26, {});
48733
+ }
48734
+ async listDeliveries(drainId, opts = {}) {
48735
+ const query = new URLSearchParams();
48736
+ if (opts.limit !== void 0) query.set("limit", String(opts.limit));
48737
+ if (opts.cursor) query.set("cursor", opts.cursor);
48738
+ if (opts.kind) query.set("kind", opts.kind);
48739
+ const suffix = query.toString() ? `?${query.toString()}` : "";
48740
+ return this.httpGet(this.drainPath(drainId, `/deliveries${suffix}`));
48741
+ }
48742
+ };
48743
+
48744
+ // src/commands/drains.mutations.ts
48745
+ init_dist();
48746
+ init_cli();
48747
+ init_cli_error();
48748
+ var CONTENT_WARNING_TEXT = "These sources carry customer content: user_message and agent_response bodies, and the debug tool envelopes attached to them. Message bodies and tool inputs may contain personal data and secrets. Lua scrubs known token shapes only \u2014 anything it does not recognise leaves the platform with the record.";
48749
+ var CONTENT_ACKNOWLEDGEMENT_TEXT = "I understand that user messages, agent responses and debug tool envelopes may contain personal data and secrets, and that Lua scrubs known token shapes only.";
48750
+ var MAX_HEADERS = 10;
48751
+ var HEADER_NAME_PATTERN = /^[A-Za-z0-9-]{1,64}$/;
48752
+ var ENDPOINT_TYPES = [
48753
+ "http",
48754
+ "otlp",
48755
+ "betterstack"
48756
+ ];
48757
+ var SITE_TYPES = [
48758
+ "datadog"
48759
+ ];
48760
+ var PHASE2_SOURCES = [
48761
+ "delivery",
48762
+ "trigger-execution",
48763
+ "job-execution",
48764
+ "workflow-run",
48765
+ "workflow-audit"
48766
+ ];
48767
+ var SELECTABLE_SOURCES = LOG_DRAIN_SOURCES.filter((source) => !PHASE2_SOURCES.includes(source));
48768
+ var NON_CONTENT_SOURCES = SELECTABLE_SOURCES.filter((source) => !LOG_DRAIN_CONTENT_SOURCES.includes(source));
48769
+ var TOKEN_ECHO_TYPES = [
48770
+ "http"
48771
+ ];
48772
+ var VERIFY_OUTCOME_TEXT = {
48773
+ ok: "verified",
48774
+ no_2xx: "the endpoint did not answer 2xx",
48775
+ token_not_echoed: "token not echoed",
48776
+ ssrf_refused: "the endpoint was refused by the SSRF guard",
48777
+ timeout: "the endpoint did not answer in time",
48778
+ error: "the verification request failed"
48779
+ };
48780
+ var DRAIN_POLL = {
48781
+ intervalMs: 1e3,
48782
+ testAttempts: 30,
48783
+ verifyAttempts: 60,
48784
+ sleep: /* @__PURE__ */ __name((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)), "sleep")
48785
+ };
48786
+ function printJson(payload) {
48787
+ console.log(JSON.stringify(payload, null, 2));
48788
+ }
48789
+ __name(printJson, "printJson");
48790
+ function emitAside(line, json) {
48791
+ if (json) process.stderr.write(`${line}
48792
+ `);
48793
+ else writeInfo(line);
48794
+ }
48795
+ __name(emitAside, "emitAside");
48796
+ function shortIso(value3) {
48797
+ if (typeof value3 !== "string" || value3.length === 0) return "\u2014";
48798
+ const date = new Date(value3);
48799
+ if (Number.isNaN(date.getTime())) return value3;
48800
+ return date.toISOString().replace(/\.\d{3}Z$/, "Z");
48801
+ }
48802
+ __name(shortIso, "shortIso");
48803
+ function trimmed(value3) {
48804
+ if (typeof value3 !== "string") return void 0;
48805
+ const text = value3.trim();
48806
+ return text.length > 0 ? text : void 0;
48807
+ }
48808
+ __name(trimmed, "trimmed");
48809
+ function parseType(raw) {
48810
+ const value3 = trimmed(raw)?.toLowerCase();
48811
+ if (!value3) return void 0;
48812
+ if (!LOG_DRAIN_TYPES_PHASE1.includes(value3)) {
48813
+ throw CliError.usage(`--type "${raw}" is not available.`, listHint("Available destinations", LOG_DRAIN_TYPES_PHASE1));
48814
+ }
48815
+ return value3;
48816
+ }
48817
+ __name(parseType, "parseType");
48818
+ function parseFormat(raw) {
48819
+ const value3 = trimmed(raw)?.toLowerCase();
48820
+ if (!value3) return void 0;
48821
+ if (!LOG_DRAIN_FORMATS.includes(value3)) {
48822
+ throw CliError.usage(`--format "${raw}" is not a drain format.`, listHint("Valid values", LOG_DRAIN_FORMATS));
48823
+ }
48824
+ return value3;
48825
+ }
48826
+ __name(parseFormat, "parseFormat");
48827
+ function parseSeverity(raw) {
48828
+ const value3 = trimmed(raw)?.toLowerCase();
48829
+ if (!value3) return void 0;
48830
+ if (!LOG_DRAIN_SEVERITIES.includes(value3)) {
48831
+ throw CliError.usage(`--min-severity "${raw}" is not a severity.`, listHint("Valid values", LOG_DRAIN_SEVERITIES));
48832
+ }
48833
+ return value3;
48834
+ }
48835
+ __name(parseSeverity, "parseSeverity");
48836
+ function splitList(raw) {
48837
+ return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
48838
+ }
48839
+ __name(splitList, "splitList");
48840
+ function parseSources(raw) {
48841
+ const value3 = trimmed(raw);
48842
+ if (!value3) return void 0;
48843
+ const requested = splitList(value3);
48844
+ if (requested.length === 0) {
48845
+ throw CliError.usage("--sources was empty \u2014 name at least one source, or leave the flag off for the default set.");
48846
+ }
48847
+ const sources = [];
48848
+ for (const source of requested) {
48849
+ if (!SELECTABLE_SOURCES.includes(source)) {
48850
+ throw CliError.usage(`--sources "${source}" is not a selectable source.`, listHint("Valid values", SELECTABLE_SOURCES));
48851
+ }
48852
+ if (!sources.includes(source)) sources.push(source);
48853
+ }
48854
+ return sources;
48855
+ }
48856
+ __name(parseSources, "parseSources");
48857
+ function parseEnvironments(raw) {
48858
+ const value3 = trimmed(raw);
48859
+ if (!value3) return void 0;
48860
+ const environments = [];
48861
+ for (const environment of splitList(value3)) {
48862
+ if (!AGENT_LOG_ENVIRONMENTS.includes(environment)) {
48863
+ throw CliError.usage(`--environments "${environment}" is not an environment.`, listHint("Valid values", AGENT_LOG_ENVIRONMENTS));
48864
+ }
48865
+ if (!environments.includes(environment)) {
48866
+ environments.push(environment);
48867
+ }
48868
+ }
48869
+ return environments.length > 0 ? environments : void 0;
48870
+ }
48871
+ __name(parseEnvironments, "parseEnvironments");
48872
+ function parseAgents(raw) {
48873
+ const value3 = trimmed(raw);
48874
+ if (!value3) return void 0;
48875
+ if (value3.toLowerCase() === "all" || value3 === "*") return "*";
48876
+ const agents = splitList(value3);
48877
+ if (agents.length === 0) {
48878
+ throw CliError.usage("--agents was empty \u2014 pass 'all' or a comma-separated list of agent ids.");
48879
+ }
48880
+ return agents;
48881
+ }
48882
+ __name(parseAgents, "parseAgents");
48883
+ function parseSampling(raw) {
48884
+ const value3 = trimmed(raw);
48885
+ if (!value3) return void 0;
48886
+ const hint = "Only debug is sampleable in phase 1, e.g. --sampling debug=0.1";
48887
+ const eq3 = value3.indexOf("=");
48888
+ if (eq3 === -1) throw CliError.usage(`--sampling "${raw}" must read <key>=<fraction>.`, hint);
48889
+ const key = value3.slice(0, eq3).trim().toLowerCase();
48890
+ const fraction = Number(value3.slice(eq3 + 1).trim());
48891
+ if (key !== "debug") throw CliError.usage(`--sampling "${key}" is not sampleable.`, hint);
48892
+ if (!Number.isFinite(fraction) || fraction <= 0 || fraction > 1) {
48893
+ throw CliError.usage(`--sampling debug must be a fraction in (0, 1] (got "${value3.slice(eq3 + 1).trim()}").`, hint);
48894
+ }
48895
+ return {
48896
+ debug: fraction
48897
+ };
48898
+ }
48899
+ __name(parseSampling, "parseSampling");
48900
+ function assertHeaderName(name, flag) {
48901
+ if (!HEADER_NAME_PATTERN.test(name)) {
48902
+ throw CliError.usage(`${flag} header name "${name}" must match ^[A-Za-z0-9-]{1,64}$.`, "A header name is a token \u2014 no spaces, no colon, no newline");
48903
+ }
48904
+ }
48905
+ __name(assertHeaderName, "assertHeaderName");
48906
+ async function resolveHeaders(options) {
48907
+ const headers = [];
48908
+ const seen = /* @__PURE__ */ new Set();
48909
+ const remember = /* @__PURE__ */ __name((header) => {
48910
+ const key = header.name.toLowerCase();
48911
+ if (seen.has(key)) {
48912
+ throw CliError.usage(`Header "${header.name}" was given twice.`, "Each header name may appear once");
48913
+ }
48914
+ seen.add(key);
48915
+ headers.push(header);
48916
+ }, "remember");
48917
+ for (const entry of options.headerFromEnv ?? []) {
48918
+ const raw = `${entry}`.trim();
48919
+ const eq3 = raw.indexOf("=");
48920
+ if (eq3 <= 0 || eq3 === raw.length - 1) {
48921
+ throw CliError.usage(`--header-from-env "${raw}" must read <name>=<ENV_VAR>.`, "The VALUE stays in the environment \u2014 e.g. --header-from-env DD-API-KEY=DATADOG_API_KEY");
48922
+ }
48923
+ const name = raw.slice(0, eq3).trim();
48924
+ const variable = raw.slice(eq3 + 1).trim();
48925
+ assertHeaderName(name, "--header-from-env");
48926
+ const value3 = process.env[variable];
48927
+ if (value3 === void 0 || value3 === "") {
48928
+ throw CliError.usage(`--header-from-env ${name}=${variable}: the environment variable ${variable} is unset or empty.`, `Export ${variable} in the step that runs this command`);
48929
+ }
48930
+ remember({
48931
+ name,
48932
+ value: value3,
48933
+ fromEnv: variable
48934
+ });
48935
+ }
48936
+ for (const entry of options.header ?? []) {
48937
+ const raw = `${entry}`.trim();
48938
+ if (raw.includes("=")) {
48939
+ throw CliError.usage(`--header takes a header NAME, not a value ("${raw.slice(0, raw.indexOf("="))}=\u2026").`, joinHint("Header values are never flags \u2014 a shell history and a CI log both keep them.", "Interactively: --header " + raw.slice(0, raw.indexOf("=")) + " (the value is prompted)", "In --ci: --header-from-env " + raw.slice(0, raw.indexOf("=")) + "=YOUR_ENV_VAR"));
48940
+ }
48941
+ assertHeaderName(raw, "--header");
48942
+ if (isCiModeEnabled()) {
48943
+ throw CliError.usage(`--header ${raw} needs an interactive prompt for its value, and --ci is set.`, `Use --header-from-env ${raw}=YOUR_ENV_VAR so the value never reaches the command line`);
48944
+ }
48945
+ const answer = await safePrompt([
48946
+ {
48947
+ type: "password",
48948
+ name: "value",
48949
+ mask: "*",
48950
+ message: `Value for header ${raw}:`,
48951
+ validate: /* @__PURE__ */ __name((input) => input && input.length > 0 ? true : "A header value cannot be empty", "validate")
48952
+ }
48953
+ ]);
48954
+ const value3 = answer?.value;
48955
+ if (!value3) throw CliError.usage(`No value was given for header ${raw}.`);
48956
+ remember({
48957
+ name: raw,
48958
+ value: value3
48959
+ });
48960
+ }
48961
+ if (headers.length > MAX_HEADERS) {
48962
+ throw CliError.usage(`A drain carries at most ${MAX_HEADERS} headers (got ${headers.length}).`);
48963
+ }
48964
+ return headers;
48965
+ }
48966
+ __name(resolveHeaders, "resolveHeaders");
48967
+ function wireHeaders(headers) {
48968
+ return headers.map(({ name, value: value3 }) => ({
48969
+ name,
48970
+ value: value3
48971
+ }));
48972
+ }
48973
+ __name(wireHeaders, "wireHeaders");
48974
+ function wantsContent(options, sources) {
48975
+ if (options.includeContent === true) return true;
48976
+ return (sources ?? []).some((source) => LOG_DRAIN_CONTENT_SOURCES.includes(source));
48977
+ }
48978
+ __name(wantsContent, "wantsContent");
48979
+ async function acknowledgeContent(options) {
48980
+ emitAside("", options.json);
48981
+ emitAside(CONTENT_WARNING_TEXT, options.json);
48982
+ emitAside("", options.json);
48983
+ emitAside(CONTENT_ACKNOWLEDGEMENT_TEXT, options.json);
48984
+ emitAside("", options.json);
48985
+ if (options.yes) return true;
48986
+ if (isCiModeEnabled()) {
48987
+ throw CliError.usage("--include-content needs the acknowledgement above; pass --yes to give it non-interactively.", "Nothing was created");
48988
+ }
48989
+ const answer = await safePrompt([
48990
+ {
48991
+ type: "confirm",
48992
+ name: "acknowledged",
48993
+ message: "Do you acknowledge this?",
48994
+ default: false
48995
+ }
48996
+ ]);
48997
+ if (!answer?.acknowledged) {
48998
+ throw CliError.usage("Content was not enabled: the acknowledgement was declined.", "Nothing was created");
48999
+ }
49000
+ return true;
49001
+ }
49002
+ __name(acknowledgeContent, "acknowledgeContent");
49003
+ function missingFlag(flag, what, hint) {
49004
+ return CliError.usage(`${flag} is required (${what}).`, joinHint(hint, "Prompting is not possible under --ci"));
49005
+ }
49006
+ __name(missingFlag, "missingFlag");
49007
+ async function promptText(message, flag, what) {
49008
+ if (isCiModeEnabled()) throw missingFlag(flag, what);
49009
+ const answer = await safePrompt([
49010
+ {
49011
+ type: "input",
49012
+ name: "value",
49013
+ message,
49014
+ validate: /* @__PURE__ */ __name((input) => input && input.trim().length > 0 ? true : "Required", "validate")
49015
+ }
49016
+ ]);
49017
+ const value3 = trimmed(answer?.value);
49018
+ if (!value3) throw missingFlag(flag, what);
49019
+ return value3;
49020
+ }
49021
+ __name(promptText, "promptText");
49022
+ async function promptChoice(message, choices, flag, what) {
49023
+ if (isCiModeEnabled()) throw missingFlag(flag, what, `Values: ${choices.join(" | ")}`);
49024
+ const answer = await safePrompt([
49025
+ {
49026
+ type: "list",
49027
+ name: "value",
49028
+ message,
49029
+ choices
49030
+ }
49031
+ ]);
49032
+ const value3 = answer?.value;
49033
+ if (!value3) throw missingFlag(flag, what, `Values: ${choices.join(" | ")}`);
49034
+ return value3;
49035
+ }
49036
+ __name(promptChoice, "promptChoice");
49037
+ async function promptSources() {
49038
+ const answer = await safePrompt([
49039
+ {
49040
+ type: "checkbox",
49041
+ name: "sources",
49042
+ message: "Which sources should this drain carry?",
49043
+ pageSize: 20,
49044
+ choices: [
49045
+ {
49046
+ type: "separator",
49047
+ line: "\u2500\u2500 Conversation content (needs the acknowledgement) \u2500\u2500"
49048
+ },
49049
+ ...LOG_DRAIN_CONTENT_SOURCES.map((source) => ({
49050
+ name: source,
49051
+ value: source,
49052
+ checked: false
49053
+ })),
49054
+ {
49055
+ type: "separator",
49056
+ line: "\u2500\u2500 Execution and platform \u2500\u2500"
49057
+ },
49058
+ ...NON_CONTENT_SOURCES.map((source) => ({
49059
+ name: source,
49060
+ value: source,
49061
+ checked: LOG_DRAIN_DEFAULT_SOURCES.includes(source)
49062
+ }))
49063
+ ],
49064
+ validate: /* @__PURE__ */ __name((chosen) => chosen.length > 0 ? true : "Pick at least one source", "validate")
49065
+ }
49066
+ ]);
49067
+ const sources = answer?.sources ?? [];
49068
+ if (sources.length === 0) throw CliError.usage("A drain needs at least one source.");
49069
+ return sources;
49070
+ }
49071
+ __name(promptSources, "promptSources");
49072
+ async function resolveDestination(type, options) {
49073
+ const endpoint = trimmed(options.endpoint);
49074
+ const site = trimmed(options.site);
49075
+ if (SITE_TYPES.includes(type)) {
49076
+ if (endpoint) {
49077
+ throw CliError.usage(`--endpoint is not accepted for ${type}: its URL is derived from --site.`, listHint("Sites", LOG_DRAIN_DATADOG_SITES));
49078
+ }
49079
+ const chosen = site ?? await promptChoice("Which site?", LOG_DRAIN_DATADOG_SITES, "--site", `${type}`);
49080
+ if (!LOG_DRAIN_DATADOG_SITES.includes(chosen)) {
49081
+ throw CliError.usage(`--site "${chosen}" is not a ${type} site.`, listHint("Sites", LOG_DRAIN_DATADOG_SITES));
49082
+ }
49083
+ return {
49084
+ site: chosen
49085
+ };
49086
+ }
49087
+ if (!ENDPOINT_TYPES.includes(type)) {
49088
+ throw CliError.usage(`This lua-cli does not know how to address a "${type}" destination yet.`, "Upgrade lua-cli, or create the drain from the dashboard");
49089
+ }
49090
+ if (site) {
49091
+ throw CliError.usage(`--site is not accepted for ${type}: give the destination URL with --endpoint.`);
49092
+ }
49093
+ const url = endpoint ?? await promptText(type === "otlp" ? "OTLP logs endpoint (the full /v1/logs URL):" : "Destination URL:", "--endpoint", `${type} sends to your own URL`);
49094
+ return {
49095
+ endpoint: url
49096
+ };
49097
+ }
49098
+ __name(resolveDestination, "resolveDestination");
49099
+ function verificationCurl(type, endpoint, headers) {
49100
+ const target = endpoint ?? "<your endpoint>";
49101
+ const headerArgs = headers.map((header) => header.fromEnv ? ` -H "${header.name}: $${header.fromEnv}" \\` : ` -H '${header.name}: <the value you entered>' \\`);
49102
+ if (TOKEN_ECHO_TYPES.includes(type)) {
49103
+ return [
49104
+ `curl -isS -X POST '${target}' \\`,
49105
+ ...headerArgs,
49106
+ ` -H 'Content-Type: application/json' \\`,
49107
+ ` -H 'X-Lua-Verify: lua-echo-check' \\`,
49108
+ ` --data '{"records":[]}' | grep -i 'x-lua-verify'`
49109
+ ];
49110
+ }
49111
+ return [
49112
+ `curl -isS -X POST '${target}' \\`,
49113
+ ...headerArgs,
49114
+ ` -H 'Content-Type: application/json' \\`,
49115
+ ` --data '[]'`
49116
+ ];
49117
+ }
49118
+ __name(verificationCurl, "verificationCurl");
49119
+ function verificationInstructions(drain, headers, options) {
49120
+ const type = drain.type;
49121
+ const json = options.json;
49122
+ emitAside("", json);
49123
+ emitAside("Before anything is delivered, Lua has to confirm you own this destination.", json);
49124
+ if (TOKEN_ECHO_TYPES.includes(type)) {
49125
+ emitAside("Lua POSTs one record carrying an X-Lua-Verify header; your receiver must answer 2xx AND", json);
49126
+ emitAside("repeat that header back on the response. Check yours first:", json);
49127
+ } else {
49128
+ emitAside("Lua POSTs one record to the intake; any 2xx from the vendor verifies the drain.", json);
49129
+ emitAside("Check the credentials reach the intake first:", json);
49130
+ }
49131
+ emitAside("", json);
49132
+ for (const line of verificationCurl(type, drain.endpoint, headers)) emitAside(` ${line}`, json);
49133
+ emitAside("", json);
49134
+ emitAside(`Then: lua drains verify ${drain.id}`, json);
49135
+ }
49136
+ __name(verificationInstructions, "verificationInstructions");
49137
+ async function createDrainCore(context, options) {
49138
+ const name = trimmed(options.name) ?? await promptText("Drain name:", "--name", "1\u201364 chars, unique per org");
49139
+ const type = parseType(options.type) ?? await promptChoice("Where should the logs go?", LOG_DRAIN_TYPES_PHASE1, "--type", "the destination kind");
49140
+ const destination = await resolveDestination(type, options);
49141
+ const format = parseFormat(options.format);
49142
+ const headers = await resolveHeaders(options);
49143
+ const flagSources = parseSources(options.sources);
49144
+ const sources = flagSources ?? (isCiModeEnabled() ? void 0 : await promptSources());
49145
+ const minSeverity = parseSeverity(options.minSeverity);
49146
+ const environments = parseEnvironments(options.environments);
49147
+ const sampling = parseSampling(options.sampling);
49148
+ const agents = parseAgents(options.agents) ?? (context.agentId ? [
49149
+ context.agentId
49150
+ ] : "*");
49151
+ const includeContent = wantsContent(options, sources);
49152
+ const includeContentAck = includeContent ? await acknowledgeContent(options) : void 0;
49153
+ const input = {
49154
+ name,
49155
+ type,
49156
+ ...destination.endpoint ? {
49157
+ endpoint: destination.endpoint
49158
+ } : {},
49159
+ ...destination.site ? {
49160
+ site: destination.site
49161
+ } : {},
49162
+ ...format ? {
49163
+ format
49164
+ } : {},
49165
+ ...headers.length > 0 ? {
49166
+ headers: wireHeaders(headers)
49167
+ } : {},
49168
+ ...sources ? {
49169
+ sources
49170
+ } : {},
49171
+ ...minSeverity ? {
49172
+ minSeverity
49173
+ } : {},
49174
+ agents,
49175
+ environments: environments ?? [
49176
+ "production"
49177
+ ],
49178
+ ...sampling ? {
49179
+ sampling
49180
+ } : {},
49181
+ ...includeContent ? {
49182
+ includeContent: true,
49183
+ includeContentAck
49184
+ } : {}
49185
+ };
49186
+ const created = await drainsCall(() => drainsApiFor(context).createDrain(input));
49187
+ const { secret, ...drain } = created;
49188
+ if (options.json) {
49189
+ printJson(secret ? {
49190
+ drain,
49191
+ secret
49192
+ } : {
49193
+ drain
49194
+ });
49195
+ } else {
49196
+ writeSuccess(`\u2705 Created drain ${drain.name} (${drain.id})`);
49197
+ writeInfo(` type ${drain.type} \xB7 ${drain.endpoint ?? drain.site ?? "\u2014"} \xB7 state ${drain.state}`);
49198
+ if (secret) {
49199
+ writeInfo("");
49200
+ writeInfo("\u{1F511} Signing secret \u2014 shown once, and never again. Store it now:");
49201
+ writeInfo(` ${secret}`);
49202
+ writeInfo(" Your receiver verifies X-Lua-Signature with it (docs: /drains/verify-signatures).");
49203
+ }
49204
+ }
49205
+ verificationInstructions(drain, headers, options);
49206
+ if (!options.json) {
49207
+ writeHintBlock({
49208
+ headline: "Next:",
49209
+ lines: [
49210
+ {
49211
+ label: "Verify",
49212
+ command: `lua drains verify ${drain.id}`
49213
+ },
49214
+ {
49215
+ label: "Send one record",
49216
+ command: `lua drains test ${drain.id}`
49217
+ }
49218
+ ]
49219
+ });
49220
+ }
49221
+ }
49222
+ __name(createDrainCore, "createDrainCore");
49223
+ async function updateDrainCore(context, drainId, options) {
49224
+ if (trimmed(options.type)) {
49225
+ throw CliError.usage("A drain cannot change type in place.", "Delete this drain and create a new one of the other type");
49226
+ }
49227
+ const patch = {};
49228
+ const name = trimmed(options.name);
49229
+ if (name) patch.name = name;
49230
+ const endpoint = trimmed(options.endpoint);
49231
+ if (endpoint) patch.endpoint = endpoint;
49232
+ const site = trimmed(options.site);
49233
+ if (site) patch.site = site;
49234
+ const format = parseFormat(options.format);
49235
+ if (format) patch.format = format;
49236
+ const headers = await resolveHeaders(options);
49237
+ if (headers.length > 0) patch.headers = wireHeaders(headers);
49238
+ const sources = parseSources(options.sources);
49239
+ if (sources) patch.sources = sources;
49240
+ const minSeverity = parseSeverity(options.minSeverity);
49241
+ if (minSeverity) patch.minSeverity = minSeverity;
49242
+ const agents = parseAgents(options.agents);
49243
+ if (agents) patch.agents = agents;
49244
+ const environments = parseEnvironments(options.environments);
49245
+ if (environments) patch.environments = environments;
49246
+ const sampling = parseSampling(options.sampling);
49247
+ if (sampling) patch.sampling = sampling;
49248
+ if (options.includeContent === false) {
49249
+ if ((sources ?? []).some((source) => LOG_DRAIN_CONTENT_SOURCES.includes(source))) {
49250
+ throw CliError.usage("--no-include-content cannot be combined with a content source in the same update.", "Send the sources first, then --no-include-content \u2014 or drop the content sources");
49251
+ }
49252
+ patch.includeContent = false;
49253
+ } else if (wantsContent(options, sources)) {
49254
+ patch.includeContent = true;
49255
+ patch.includeContentAck = await acknowledgeContent(options);
49256
+ }
49257
+ if (Object.keys(patch).length === 0) {
49258
+ throw CliError.usage("Nothing to update \u2014 no field flags were given.", listHint("Changeable", [
49259
+ "--name",
49260
+ "--endpoint",
49261
+ "--site",
49262
+ "--format",
49263
+ "--header / --header-from-env",
49264
+ "--sources",
49265
+ "--min-severity",
49266
+ "--agents",
49267
+ "--environments",
49268
+ "--sampling",
49269
+ "--include-content"
49270
+ ]));
49271
+ }
49272
+ const drain = await drainsCall(() => drainsApiFor(context).updateDrain(drainId, patch));
49273
+ if (options.json) {
49274
+ printJson({
49275
+ drain
49276
+ });
49277
+ return;
49278
+ }
49279
+ writeSuccess(`\u2705 Updated ${drain.name} (${drain.id})`);
49280
+ writeInfo(` state ${drain.state}`);
49281
+ if (drain.state === "pending_verification") {
49282
+ writeInfo(" The destination changed, so delivery is paused until the drain is verified again.");
49283
+ writeInfo(" Records keep buffering while you do.");
49284
+ writeHintBlock({
49285
+ headline: "Next:",
49286
+ lines: [
49287
+ {
49288
+ label: "Verify",
49289
+ command: `lua drains verify ${drain.id}`
49290
+ }
49291
+ ]
49292
+ });
49293
+ }
49294
+ }
49295
+ __name(updateDrainCore, "updateDrainCore");
49296
+ async function deleteDrainCore(context, drainId, options) {
49297
+ if (!options.yes) {
49298
+ if (isCiModeEnabled()) {
49299
+ throw CliError.usage(`Deleting ${drainId} needs a confirmation; pass --yes to give it non-interactively.`, "Nothing was deleted");
49300
+ }
49301
+ writeInfo(`Deleting a drain purges the destination's stored credentials and drops its queued records.`);
49302
+ const answer = await safePrompt([
49303
+ {
49304
+ type: "confirm",
49305
+ name: "confirmed",
49306
+ message: `Delete drain ${drainId}?`,
49307
+ default: false
49308
+ }
49309
+ ]);
49310
+ if (!answer?.confirmed) {
49311
+ throw CliError.usage("Cancelled \u2014 nothing was deleted.");
49312
+ }
49313
+ }
49314
+ await drainsCall(() => drainsApiFor(context).deleteDrain(drainId));
49315
+ if (options.json) {
49316
+ printJson({
49317
+ id: drainId,
49318
+ deleted: true
49319
+ });
49320
+ return;
49321
+ }
49322
+ writeSuccess(`\u2705 Deleted drain ${drainId}`);
49323
+ writeInfo(" Stored credentials were purged and queued records were dropped.");
49324
+ }
49325
+ __name(deleteDrainCore, "deleteDrainCore");
49326
+ async function awaitTestDelivery(api, drainId, batchId) {
49327
+ for (let attempt = 0; attempt < DRAIN_POLL.testAttempts; attempt++) {
49328
+ await DRAIN_POLL.sleep(DRAIN_POLL.intervalMs);
49329
+ const page = await drainsCall(() => api.listDeliveries(drainId, {
49330
+ kind: "test",
49331
+ limit: 20
49332
+ }));
49333
+ const row2 = (page.deliveries ?? []).find((delivery) => delivery.batchId === batchId);
49334
+ if (row2) return row2;
49335
+ }
49336
+ return null;
49337
+ }
49338
+ __name(awaitTestDelivery, "awaitTestDelivery");
49339
+ async function testDrainCore(context, drainId, options) {
49340
+ const api = drainsApiFor(context);
49341
+ const started = await drainsCall(() => api.testDrain(drainId));
49342
+ if (!options.json) writeInfo(`Sent one test record to ${drainId} (batch ${started.batchId}) \u2014 waiting for it\u2026`);
49343
+ const attempt = await awaitTestDelivery(api, drainId, started.batchId);
49344
+ const delivered = attempt?.ok === true;
49345
+ if (options.json) {
49346
+ printJson({
49347
+ batchId: started.batchId,
49348
+ statusCode: attempt?.statusCode ?? null,
49349
+ latencyMs: attempt?.latencyMs ?? null,
49350
+ responseExcerpt: attempt?.responseExcerpt ?? null,
49351
+ ok: delivered,
49352
+ errorClass: attempt?.errorClass ?? null,
49353
+ ...attempt ? {} : {
49354
+ pending: true
49355
+ }
49356
+ });
49357
+ } else if (!attempt) {
49358
+ writeInfo(`\u23F3 No delivery attempt recorded yet for batch ${started.batchId}.`);
49359
+ writeHintBlock({
49360
+ headline: "Read it when it lands:",
49361
+ lines: [
49362
+ {
49363
+ label: "Attempts",
49364
+ command: `lua drains deliveries ${drainId} --kind test`
49365
+ }
49366
+ ],
49367
+ when: "partial"
49368
+ });
49369
+ } else {
49370
+ const status = attempt.statusCode === void 0 ? "\u2014" : String(attempt.statusCode);
49371
+ const latency = attempt.latencyMs === void 0 ? "\u2014" : `${attempt.latencyMs} ms`;
49372
+ if (delivered) writeSuccess(`\u2705 Delivered \u2014 HTTP ${status} in ${latency}`);
49373
+ else writeInfo(`Not delivered \u2014 HTTP ${status} in ${latency}${attempt.errorClass ? ` (${attempt.errorClass})` : ""}`);
49374
+ if (attempt.responseExcerpt) writeInfo(` response: ${attempt.responseExcerpt}`);
49375
+ }
49376
+ if (!delivered) process.exitCode = CLI_EXIT.ERROR;
49377
+ }
49378
+ __name(testDrainCore, "testDrainCore");
49379
+ async function awaitVerification(api, drainId, previousAttemptedAt) {
49380
+ for (let attempt = 0; attempt < DRAIN_POLL.verifyAttempts; attempt++) {
49381
+ await DRAIN_POLL.sleep(DRAIN_POLL.intervalMs);
49382
+ const drain = await drainsCall(() => api.getDrain(drainId));
49383
+ const verification = drain.verification;
49384
+ if (verification?.outcome && verification.attemptedAt !== previousAttemptedAt) return drain;
49385
+ }
49386
+ return null;
49387
+ }
49388
+ __name(awaitVerification, "awaitVerification");
49389
+ async function verifyDrainCore(context, drainId, options) {
49390
+ const api = drainsApiFor(context);
49391
+ const before = await drainsCall(() => api.getDrain(drainId));
49392
+ const started = await drainsCall(() => api.verifyDrain(drainId));
49393
+ if (!options.json) writeInfo(`Verifying ${drainId} \u2014 waiting for your endpoint to answer\u2026`);
49394
+ const drain = await awaitVerification(api, drainId, before.verification?.attemptedAt);
49395
+ const verification = drain?.verification;
49396
+ const outcome = verification?.outcome;
49397
+ const verified = outcome === "ok";
49398
+ const line = outcome ? VERIFY_OUTCOME_TEXT[outcome] : "no result yet";
49399
+ if (options.json) {
49400
+ printJson({
49401
+ verificationId: started.verificationId,
49402
+ expiresAt: started.expiresAt,
49403
+ outcome: outcome ?? null,
49404
+ message: line,
49405
+ statusCode: verification?.statusCode ?? null,
49406
+ latencyMs: verification?.latencyMs ?? null,
49407
+ responseExcerpt: verification?.responseExcerpt ?? null,
49408
+ state: drain?.state ?? null,
49409
+ verifiedAt: drain?.verifiedAt ?? null,
49410
+ verified,
49411
+ ...drain ? {} : {
49412
+ pending: true
49413
+ }
49414
+ });
49415
+ } else {
49416
+ const status = verification?.statusCode === void 0 ? "\u2014" : String(verification.statusCode);
49417
+ const latency = verification?.latencyMs === void 0 ? "\u2014" : `${verification.latencyMs} ms`;
49418
+ if (verified) {
49419
+ writeSuccess(`\u2705 Verified \u2014 HTTP ${status} in ${latency}`);
49420
+ writeInfo(` state ${drain?.state ?? "\u2014"} \xB7 verifiedAt ${shortIso(drain?.verifiedAt)}`);
49421
+ } else if (!drain) {
49422
+ writeInfo(`\u23F3 ${line} (attempt ${started.verificationId}, expires ${shortIso(started.expiresAt)}).`);
49423
+ writeHintBlock({
49424
+ headline: "Read it when it lands:",
49425
+ lines: [
49426
+ {
49427
+ label: "Drain",
49428
+ command: `lua drains status ${drainId}`
49429
+ }
49430
+ ],
49431
+ when: "partial"
49432
+ });
49433
+ } else {
49434
+ writeInfo(`Not verified: ${line} \u2014 HTTP ${status} in ${latency}`);
49435
+ if (verification?.responseExcerpt) writeInfo(` response: ${verification.responseExcerpt}`);
49436
+ if (outcome === "token_not_echoed") {
49437
+ writeInfo(" Your receiver answered, but did not repeat the X-Lua-Verify header on its response.");
49438
+ }
49439
+ }
49440
+ }
49441
+ if (!verified) process.exitCode = CLI_EXIT.ERROR;
49442
+ }
49443
+ __name(verifyDrainCore, "verifyDrainCore");
49444
+ async function pauseDrainCore(context, drainId, options) {
49445
+ const reason = trimmed(options.reason);
49446
+ const drain = await drainsCall(() => drainsApiFor(context).pauseDrain(drainId, reason));
49447
+ if (options.json) {
49448
+ printJson({
49449
+ drain
49450
+ });
49451
+ return;
49452
+ }
49453
+ writeSuccess(`\u2705 Paused ${drain.name} (${drain.id})`);
49454
+ writeInfo(` state ${drain.state}${reason ? ` \xB7 reason "${reason}"` : ""}`);
49455
+ writeInfo(" Records keep buffering while the drain is paused; nothing is delivered.");
49456
+ writeHintBlock({
49457
+ headline: "Next:",
49458
+ lines: [
49459
+ {
49460
+ label: "Resume",
49461
+ command: `lua drains resume ${drain.id}`
49462
+ }
49463
+ ]
49464
+ });
49465
+ }
49466
+ __name(pauseDrainCore, "pauseDrainCore");
49467
+ async function resumeDrainCore(context, drainId, options) {
49468
+ const drain = await drainsCall(() => drainsApiFor(context).resumeDrain(drainId));
49469
+ if (options.json) {
49470
+ printJson({
49471
+ drain
49472
+ });
49473
+ return;
49474
+ }
49475
+ writeSuccess(`\u2705 Resumed ${drain.name} (${drain.id})`);
49476
+ writeInfo(` state ${drain.state} \u2014 a resumed drain proves itself before it reads healthy again.`);
49477
+ writeHintBlock({
49478
+ headline: "Next:",
49479
+ lines: [
49480
+ {
49481
+ label: "Health",
49482
+ command: `lua drains status ${drain.id}`
49483
+ }
49484
+ ]
49485
+ });
49486
+ }
49487
+ __name(resumeDrainCore, "resumeDrainCore");
49488
+ async function rotateSecretCore(context, drainId, options) {
49489
+ const finalize = options.finalize === true;
49490
+ const answer = await drainsCall(() => drainsApiFor(context).rotateSecret(drainId, finalize));
49491
+ if (finalize) {
49492
+ const drain = answer;
49493
+ if (options.json) {
49494
+ printJson({
49495
+ drain
49496
+ });
49497
+ return;
49498
+ }
49499
+ writeSuccess(`\u2705 Rotation window closed for ${drain.name ?? drainId}`);
49500
+ writeInfo(" Deliveries now carry the new signature only.");
49501
+ return;
49502
+ }
49503
+ const rotated = answer;
49504
+ if (options.json) {
49505
+ printJson({
49506
+ secret: rotated.secret,
49507
+ rotation: rotated.rotation
49508
+ });
49509
+ return;
49510
+ }
49511
+ writeSuccess(`\u2705 Rotated the signing secret for ${drainId}`);
49512
+ writeInfo("");
49513
+ writeInfo("\u{1F511} New signing secret \u2014 shown once, and never again. Store it now:");
49514
+ writeInfo(` ${rotated.secret}`);
49515
+ writeInfo("");
49516
+ writeInfo(` Both secrets are accepted until ${shortIso(rotated.rotation?.expiresAt)}; every batch in that`);
49517
+ writeInfo(" window carries two signatures, so deploy the new value and then close the window.");
49518
+ writeHintBlock({
49519
+ headline: "When the receiver has the new value:",
49520
+ lines: [
49521
+ {
49522
+ label: "Close early",
49523
+ command: `lua drains rotate-secret ${drainId} --finalize`
49524
+ }
49525
+ ]
49526
+ });
49527
+ }
49528
+ __name(rotateSecretCore, "rotateSecretCore");
49529
+
49530
+ // src/commands/drains.ts
49531
+ var DRAIN_ACTIONS = [
49532
+ "list",
49533
+ "status",
49534
+ "deliveries",
49535
+ "create",
49536
+ "update",
49537
+ "delete",
49538
+ "test",
49539
+ "verify",
49540
+ "pause",
49541
+ "resume",
49542
+ "rotate-secret"
49543
+ ];
49544
+ var MISSING_SCOPE_MESSAGE = "Missing scope: logs:manage. Create a key with that scope (lua auth key) or ask an org admin.";
49545
+ var DRAINS_FAILING_EXIT = 2;
49546
+ var DELIVERY_KINDS = [
49547
+ "batch",
49548
+ "test",
49549
+ "verify",
49550
+ "heartbeat",
49551
+ "dropped",
49552
+ "truncated"
49553
+ ];
49554
+ var DEFAULT_DELIVERY_LIMIT = 20;
49555
+ var MAX_DELIVERY_LIMIT = 100;
49556
+ var MAX_TABLE_WIDTH = 100;
49557
+ async function resolveDrainsContext(options) {
49558
+ const apiKey = await requireAuth();
49559
+ const flagOrg = typeof options.org === "string" ? options.org.trim() : "";
49560
+ if (flagOrg) return {
49561
+ orgId: flagOrg,
49562
+ apiKey
49563
+ };
49564
+ const config = readYamlConfig();
49565
+ const projectOrg = config?.agent?.orgId;
49566
+ if (projectOrg) return {
49567
+ orgId: projectOrg,
49568
+ agentId: config?.agent?.agentId,
49569
+ apiKey
49570
+ };
49571
+ return {
49572
+ orgId: await orgFromCredential(apiKey),
49573
+ apiKey
49574
+ };
49575
+ }
49576
+ __name(resolveDrainsContext, "resolveDrainsContext");
49577
+ async function orgFromCredential(apiKey) {
49578
+ const { authorization } = await resolveCredentialOperationalContext(apiKey);
49579
+ const orgs = (authorization.orgs ?? []).filter((org) => !org.archived);
49580
+ if (orgs.length === 1) return orgs[0].orgId;
49581
+ if (orgs.length === 0) {
49582
+ throw CliError.usage("This credential does not reach any organization, so there is no drain to read.", "Ask an org admin for access, or run the command from a Lua project directory");
49583
+ }
49584
+ throw CliError.usage(`This credential reaches ${orgs.length} organizations \u2014 name one with --org <id>.`, joinHint(listHint("Organizations", orgs.map((org) => `${org.orgId}${org.name ? ` \u2014 ${org.name}` : ""}`)), "Inside a Lua project directory the org comes from lua.skill.yaml"));
49585
+ }
49586
+ __name(orgFromCredential, "orgFromCredential");
49587
+ function drainsApiFor(context) {
49588
+ return new DrainsApi(BASE_URLS.API, context.apiKey, context.orgId);
49589
+ }
49590
+ __name(drainsApiFor, "drainsApiFor");
49591
+ function drainRefusalMessage(error) {
49592
+ const message = error?.message || "The log drains API refused the request.";
49593
+ const code = error?.code;
49594
+ const field = error?.field;
49595
+ const line = code ? `${code}: ${message}` : message;
49596
+ return field ? `${line} (field: ${field})` : line;
49597
+ }
49598
+ __name(drainRefusalMessage, "drainRefusalMessage");
49599
+ async function drainsCall(call) {
49600
+ let response;
49601
+ try {
49602
+ response = await call();
49603
+ } catch (error) {
49604
+ if (CliError.isCliError(error) && error.statusCode === 403) {
49605
+ throw CliError.forbidden(MISSING_SCOPE_MESSAGE);
49606
+ }
49607
+ throw error;
49608
+ }
49609
+ if (response.success && response.data !== void 0) return response.data;
49610
+ if (response.error?.statusCode === 403) throw CliError.forbidden(MISSING_SCOPE_MESSAGE);
49611
+ throw CliError.fromStatus(response.error?.statusCode, drainRefusalMessage(response.error), void 0, apiErrorDetail(response.error));
49612
+ }
49613
+ __name(drainsCall, "drainsCall");
49614
+ function printJson2(payload) {
49615
+ console.log(JSON.stringify(payload, null, 2));
49616
+ }
49617
+ __name(printJson2, "printJson");
49618
+ function tableOverhead(columns) {
49619
+ return 3 * columns + 1;
49620
+ }
49621
+ __name(tableOverhead, "tableOverhead");
49622
+ function clip(value3, width) {
49623
+ if (value3.length <= width) return value3;
49624
+ return width <= 1 ? value3.slice(0, width) : `${value3.slice(0, width - 1)}\u2026`;
49625
+ }
49626
+ __name(clip, "clip");
49627
+ function renderFittedTable(headers, rows3, options = {}) {
49628
+ if (rows3.length === 0) return renderTable(headers, rows3);
49629
+ const { maxWidth = MAX_TABLE_WIDTH, shrinkFirst = [], softFloor = 12 } = options;
49630
+ const budget = maxWidth - tableOverhead(headers.length);
49631
+ const hardFloors = headers.map((header) => header.length);
49632
+ const widths = headers.map((header, i) => Math.max(header.length, ...rows3.map((row2) => (row2[i] ?? "").length)));
49633
+ let total = widths.reduce((sum, width) => sum + width, 0);
49634
+ const shrink = /* @__PURE__ */ __name((candidates, floorOf) => {
49635
+ for (; ; ) {
49636
+ if (total <= budget) return;
49637
+ let widest = -1;
49638
+ for (const i of candidates) {
49639
+ if (widths[i] <= floorOf(i)) continue;
49640
+ if (widest === -1 || widths[i] > widths[widest]) widest = i;
49641
+ }
49642
+ if (widest === -1) return;
49643
+ widths[widest] -= 1;
49644
+ total -= 1;
49645
+ }
49646
+ }, "shrink");
49647
+ shrink(shrinkFirst, (i) => Math.max(hardFloors[i], softFloor));
49648
+ shrink(shrinkFirst, (i) => hardFloors[i]);
49649
+ shrink(headers.map((_, i) => i), (i) => hardFloors[i]);
49650
+ return renderTable(headers, rows3.map((row2) => row2.map((cell, i) => clip(cell ?? "", widths[i]))));
49651
+ }
49652
+ __name(renderFittedTable, "renderFittedTable");
49653
+ function shortIso2(value3) {
49654
+ if (typeof value3 !== "string" || value3.length === 0) return "\u2014";
49655
+ const date = new Date(value3);
49656
+ if (Number.isNaN(date.getTime())) return value3;
49657
+ return date.toISOString().replace(/\.\d{3}Z$/, "Z");
49658
+ }
49659
+ __name(shortIso2, "shortIso");
49660
+ function environmentsOf(drain) {
49661
+ const environments = drain.selectors?.environments ?? [];
49662
+ return environments.length > 0 ? environments.join(",") : "\u2014";
49663
+ }
49664
+ __name(environmentsOf, "environmentsOf");
49665
+ function agentsOf(drain) {
49666
+ const agents = drain.selectors?.agents;
49667
+ if (agents === "*" || agents === void 0) return "all";
49668
+ return String(agents.length);
49669
+ }
49670
+ __name(agentsOf, "agentsOf");
49671
+ function drainsTable(drains) {
49672
+ return renderFittedTable(
49673
+ [
49674
+ "NAME",
49675
+ "TYPE",
49676
+ "STATE",
49677
+ "ENV",
49678
+ "AGENTS",
49679
+ "BACKLOG",
49680
+ "LAST SUCCESS"
49681
+ ],
49682
+ drains.map((drain) => [
49683
+ drain.name ?? "",
49684
+ drain.type ?? "",
49685
+ drain.state ?? "",
49686
+ environmentsOf(drain),
49687
+ agentsOf(drain),
49688
+ String(drain.health?.backlog ?? 0),
49689
+ shortIso2(drain.health?.lastSuccessAt)
49690
+ ]),
49691
+ {
49692
+ shrinkFirst: [
49693
+ 0,
49694
+ 3
49695
+ ]
49696
+ }
49697
+ // NAME and ENV give way before a state, a count or a timestamp
49698
+ );
49699
+ }
49700
+ __name(drainsTable, "drainsTable");
49701
+ function statusTable(drains) {
49702
+ return renderFittedTable([
49703
+ "NAME",
49704
+ "STATE",
49705
+ "BACKLOG",
49706
+ "DELIVERED",
49707
+ "DROPPED",
49708
+ "REJECTED",
49709
+ "P50 MS",
49710
+ "QUOTA",
49711
+ "LAST SUCCESS"
49712
+ ], drains.map((drain) => [
49713
+ drain.name ?? "",
49714
+ drain.state ?? "",
49715
+ String(drain.health?.backlog ?? 0),
49716
+ String(drain.health?.deliveredCount24h ?? 0),
49717
+ String(drain.health?.droppedCount24h ?? 0),
49718
+ String(drain.health?.rejectedCount24h ?? 0),
49719
+ drain.health?.p50LatencyMs === null || drain.health?.p50LatencyMs === void 0 ? "\u2014" : String(drain.health.p50LatencyMs),
49720
+ quotaRatio(drain),
49721
+ shortIso2(drain.health?.lastSuccessAt)
49722
+ ]), {
49723
+ shrinkFirst: [
49724
+ 0
49725
+ ]
49726
+ });
49727
+ }
49728
+ __name(statusTable, "statusTable");
49729
+ function quotaRatio(drain) {
49730
+ const quota = drain.quota;
49731
+ if (!quota || !quota.eventsPerDay) return "\u2014";
49732
+ const ratio = quota.eventsPerDay > 0 ? (quota.usedEvents ?? 0) / quota.eventsPerDay : 0;
49733
+ const percent = Math.round(ratio * 1e3) / 10;
49734
+ return quota.degradation && quota.degradation !== "none" ? `${percent}% ${quota.degradation}` : `${percent}%`;
49735
+ }
49736
+ __name(quotaRatio, "quotaRatio");
49737
+ function printDrainDetail(drain) {
49738
+ const health = drain.health ?? {};
49739
+ const quota = drain.quota ?? {};
49740
+ const rows3 = [
49741
+ [
49742
+ "type",
49743
+ drain.type ?? "\u2014"
49744
+ ],
49745
+ [
49746
+ "state",
49747
+ drain.state ?? "\u2014"
49748
+ ],
49749
+ [
49750
+ "endpoint",
49751
+ drain.endpoint ?? "\u2014"
49752
+ ],
49753
+ [
49754
+ "site",
49755
+ drain.site ?? "\u2014"
49756
+ ],
49757
+ [
49758
+ "format",
49759
+ drain.format ?? "\u2014"
49760
+ ],
49761
+ [
49762
+ "environments",
49763
+ environmentsOf(drain)
49764
+ ],
49765
+ [
49766
+ "agents",
49767
+ drain.selectors?.agents === "*" ? "all" : (drain.selectors?.agents ?? []).join(",") || "\u2014"
49768
+ ],
49769
+ [
49770
+ "minSeverity",
49771
+ drain.selectors?.minSeverity ?? "\u2014"
49772
+ ],
49773
+ [
49774
+ "includeContent",
49775
+ String(drain.selectors?.includeContent ?? false)
49776
+ ],
49777
+ [
49778
+ "verifiedAt",
49779
+ shortIso2(drain.verifiedAt)
49780
+ ],
49781
+ [
49782
+ "lastSuccessAt",
49783
+ shortIso2(health.lastSuccessAt)
49784
+ ],
49785
+ [
49786
+ "lastFailureAt",
49787
+ shortIso2(health.lastFailureAt)
49788
+ ],
49789
+ [
49790
+ "lastStatusCode",
49791
+ health.lastStatusCode === void 0 ? "\u2014" : String(health.lastStatusCode)
49792
+ ],
49793
+ [
49794
+ "lastError",
49795
+ health.lastError ?? "\u2014"
49796
+ ],
49797
+ [
49798
+ "backlog",
49799
+ String(health.backlog ?? 0)
49800
+ ],
49801
+ [
49802
+ "backlogBytes",
49803
+ String(health.backlogBytes ?? 0)
49804
+ ],
49805
+ [
49806
+ "deliveredCount24h",
49807
+ String(health.deliveredCount24h ?? 0)
49808
+ ],
49809
+ [
49810
+ "droppedCount24h",
49811
+ String(health.droppedCount24h ?? 0)
49812
+ ],
49813
+ [
49814
+ "rejectedCount24h",
49815
+ String(health.rejectedCount24h ?? 0)
49816
+ ],
49817
+ [
49818
+ "p50LatencyMs",
49819
+ health.p50LatencyMs === null || health.p50LatencyMs === void 0 ? "\u2014" : String(health.p50LatencyMs)
49820
+ ],
49821
+ [
49822
+ "scrubHits24h",
49823
+ String(health.scrubHits24h ?? 0)
49824
+ ],
49825
+ [
49826
+ "pauseReason",
49827
+ health.pauseReason ?? "\u2014"
49828
+ ],
49829
+ [
49830
+ "usedEvents",
49831
+ `${quota.usedEvents ?? 0} / ${quota.eventsPerDay ?? 0}`
49832
+ ],
49833
+ [
49834
+ "usedBytes",
49835
+ `${quota.usedBytes ?? 0} / ${quota.bytesPerDay ?? 0}`
49836
+ ],
49837
+ [
49838
+ "degradation",
49839
+ quota.degradation ?? "\u2014"
49840
+ ],
49841
+ [
49842
+ "resetsAt",
49843
+ shortIso2(quota.resetsAt)
49844
+ ]
49845
+ ];
49846
+ const label2 = Math.max(...rows3.map(([key]) => key.length));
49847
+ console.log(`
49848
+ \u{1F6B0} ${drain.name} (${drain.id})`);
49849
+ for (const [key, value3] of rows3) {
49850
+ console.log(` ${key.padEnd(label2)} ${clip(value3, MAX_TABLE_WIDTH - label2 - 5)}`);
49851
+ }
49852
+ console.log("");
49853
+ }
49854
+ __name(printDrainDetail, "printDrainDetail");
49855
+ function statusView(drain) {
49856
+ return {
49857
+ id: drain.id,
49858
+ name: drain.name,
49859
+ state: drain.state,
49860
+ health: drain.health,
49861
+ quota: drain.quota
49862
+ };
49863
+ }
49864
+ __name(statusView, "statusView");
49865
+ function str(value3) {
49866
+ if (value3 === void 0 || value3 === null || value3 === "") return "\u2014";
49867
+ return String(value3);
49868
+ }
49869
+ __name(str, "str");
49870
+ function deliveriesTable(deliveries) {
49871
+ return renderFittedTable(
49872
+ [
49873
+ "STARTED",
49874
+ "KIND",
49875
+ "TRY",
49876
+ "OK",
49877
+ "STATUS",
49878
+ "MS",
49879
+ "RECORDS",
49880
+ "BYTES",
49881
+ "ERROR"
49882
+ ],
49883
+ deliveries.map((delivery) => [
49884
+ shortIso2(delivery.startedAt),
49885
+ str(delivery.kind),
49886
+ str(delivery.attempt),
49887
+ delivery.ok === true ? "yes" : "no",
49888
+ str(delivery.statusCode),
49889
+ str(delivery.latencyMs),
49890
+ str(delivery.recordCount),
49891
+ str(delivery.bytes),
49892
+ str(delivery.errorClass)
49893
+ ]),
49894
+ {
49895
+ shrinkFirst: [
49896
+ 8
49897
+ ],
49898
+ softFloor: 8
49899
+ }
49900
+ // the error class gives way before the timings do
49901
+ );
49902
+ }
49903
+ __name(deliveriesTable, "deliveriesTable");
49904
+ async function listDrainsCore(context, options) {
49905
+ const payload = await drainsCall(() => drainsApiFor(context).listDrains());
49906
+ if (options.json) {
49907
+ printJson2(payload);
49908
+ return;
49909
+ }
49910
+ const drains = payload.drains ?? [];
49911
+ if (drains.length === 0) {
49912
+ writeInfo(`No log drains in ${context.orgId}.`);
49913
+ writeHintBlock({
49914
+ headline: "Send this org\u2019s logs somewhere you own:",
49915
+ lines: [
49916
+ {
49917
+ label: "Create one",
49918
+ command: "lua drains create"
49919
+ }
49920
+ ]
49921
+ });
49922
+ return;
49923
+ }
49924
+ console.log(drainsTable(drains));
49925
+ writeHintBlock({
49926
+ headline: "Next:",
49927
+ lines: [
49928
+ {
49929
+ label: "Health",
49930
+ command: "lua drains status"
49931
+ },
49932
+ {
49933
+ label: "Attempts",
49934
+ command: `lua drains deliveries ${drains[0].id}`
49935
+ }
49936
+ ]
49937
+ });
49938
+ }
49939
+ __name(listDrainsCore, "listDrainsCore");
49940
+ async function statusDrainsCore(context, drainId, options) {
49941
+ const api = drainsApiFor(context);
49942
+ const drains = drainId ? [
49943
+ await drainsCall(() => api.getDrain(drainId))
49944
+ ] : (await drainsCall(() => api.listDrains())).drains ?? [];
49945
+ if (options.json) {
49946
+ printJson2({
49947
+ drains: drains.map(statusView)
49948
+ });
49949
+ } else if (drains.length === 0) {
49950
+ writeInfo(`No log drains in ${context.orgId}.`);
49951
+ } else if (drainId) {
49952
+ printDrainDetail(drains[0]);
49953
+ } else {
49954
+ console.log(statusTable(drains));
49955
+ }
49956
+ const failing = drains.filter((drain) => drain.state === "failing");
49957
+ if (failing.length > 0) {
49958
+ if (!options.json) {
49959
+ writeInfo(`\u26A0\uFE0F ${failing.length} drain(s) failing: ${failing.map((drain) => drain.name).join(", ")}`);
49960
+ writeHintBlock({
49961
+ headline: "Why a drain is failing:",
49962
+ lines: [
49963
+ {
49964
+ label: "Attempts",
49965
+ command: `lua drains deliveries ${failing[0].id} --kind batch`
49966
+ }
49967
+ ],
49968
+ when: "partial"
49969
+ });
49970
+ }
49971
+ process.exitCode = DRAINS_FAILING_EXIT;
49972
+ }
49973
+ }
49974
+ __name(statusDrainsCore, "statusDrainsCore");
49975
+ async function deliveriesCore(context, drainId, options) {
49976
+ const limit = parseLimit(options.limit);
49977
+ const kind = parseKind(options.kind);
49978
+ const page = await drainsCall(() => drainsApiFor(context).listDeliveries(drainId, {
49979
+ limit,
49980
+ ...kind ? {
49981
+ kind
49982
+ } : {}
49983
+ }));
49984
+ if (options.json) {
49985
+ printJson2(page);
49986
+ return;
49987
+ }
49988
+ const deliveries = page.deliveries ?? [];
49989
+ if (deliveries.length === 0) {
49990
+ writeInfo(`No delivery attempts recorded for ${drainId}${kind ? ` (kind ${kind})` : ""}.`);
49991
+ return;
49992
+ }
49993
+ console.log(deliveriesTable(deliveries));
49994
+ if (page.nextCursor) {
49995
+ writeInfo(`\u2026 more attempts available \u2014 raise --limit (max ${MAX_DELIVERY_LIMIT}) or read --json for the cursor.`);
49996
+ }
49997
+ }
49998
+ __name(deliveriesCore, "deliveriesCore");
49999
+ function parseLimit(raw) {
50000
+ if (raw === void 0 || raw === null || `${raw}`.trim() === "") return DEFAULT_DELIVERY_LIMIT;
50001
+ const limit = Number(raw);
50002
+ if (!Number.isInteger(limit) || limit < 1 || limit > MAX_DELIVERY_LIMIT) {
50003
+ throw CliError.usage(`--limit must be a whole number between 1 and ${MAX_DELIVERY_LIMIT} (got "${raw}").`, `Default is ${DEFAULT_DELIVERY_LIMIT}`);
50004
+ }
50005
+ return limit;
50006
+ }
50007
+ __name(parseLimit, "parseLimit");
50008
+ function parseKind(raw) {
50009
+ if (raw === void 0 || raw === null || `${raw}`.trim() === "") return void 0;
50010
+ const kind = `${raw}`.trim().toLowerCase();
50011
+ if (!DELIVERY_KINDS.includes(kind)) {
50012
+ throw CliError.usage(`--kind "${raw}" is not a delivery kind.`, listHint("Valid values", DELIVERY_KINDS));
50013
+ }
50014
+ return kind;
50015
+ }
50016
+ __name(parseKind, "parseKind");
50017
+ async function drainsCommand(action, drainId, cmdObj) {
50018
+ return withErrorHandling(
50019
+ async () => {
50020
+ const options = cmdObj ?? {};
50021
+ const verb = action ? validateOrSuggest("drains.action", action) : "list";
50022
+ const needsId = /* @__PURE__ */ __name((v) => v === "deliveries" || v === "update" || v === "delete" || v === "test" || v === "verify" || v === "pause" || v === "resume" || v === "rotate-secret", "needsId");
50023
+ if (needsId(verb) && !drainId) {
50024
+ throw CliError.usage(`lua drains ${verb} needs a drain id.`, listHint("Run", [
50025
+ "lua drains list"
50026
+ ]));
50027
+ }
50028
+ const context = await resolveDrainsContext(options);
50029
+ switch (verb) {
50030
+ case "list":
50031
+ await listDrainsCore(context, options);
50032
+ break;
50033
+ case "status":
50034
+ await statusDrainsCore(context, drainId, options);
50035
+ break;
50036
+ case "deliveries":
50037
+ await deliveriesCore(context, drainId, options);
50038
+ break;
50039
+ case "create":
50040
+ await createDrainCore(context, options);
50041
+ break;
50042
+ case "update":
50043
+ await updateDrainCore(context, drainId, options);
50044
+ break;
50045
+ case "delete":
50046
+ await deleteDrainCore(context, drainId, options);
50047
+ break;
50048
+ case "test":
50049
+ await testDrainCore(context, drainId, options);
50050
+ break;
50051
+ case "verify":
50052
+ await verifyDrainCore(context, drainId, options);
50053
+ break;
50054
+ case "pause":
50055
+ await pauseDrainCore(context, drainId, options);
50056
+ break;
50057
+ case "resume":
50058
+ await resumeDrainCore(context, drainId, options);
50059
+ break;
50060
+ case "rotate-secret":
50061
+ await rotateSecretCore(context, drainId, options);
50062
+ break;
50063
+ default:
50064
+ throw CliError.usage(`Unknown drains action '${action}'.`, listHint("Try", DRAIN_ACTIONS));
50065
+ }
50066
+ trackEvent("cli_drains_action", {
50067
+ action: verb,
50068
+ non_interactive: !!action
50069
+ });
50070
+ },
50071
+ "drains",
50072
+ // LUA-803: a refusal that escapes a verb under `--json` is the typed envelope on stdout,
50073
+ // never the `✖` line — the one shape a script parses.
50074
+ {
50075
+ json: /* @__PURE__ */ __name(() => !!cmdObj?.json, "json")
50076
+ }
50077
+ );
50078
+ }
50079
+ __name(drainsCommand, "drainsCommand");
50080
+
48085
50081
  // src/commands/completion.ts
48086
50082
  init_cli();
48087
50083
  init_analytics();
@@ -49422,10 +51418,10 @@ function truncate2(s, n2) {
49422
51418
  return s.length <= n2 ? s : s.slice(0, Math.max(0, n2 - 1)) + "\u2026";
49423
51419
  }
49424
51420
  __name(truncate2, "truncate");
49425
- function printJson(report2) {
51421
+ function printJson3(report2) {
49426
51422
  console.log(JSON.stringify(report2));
49427
51423
  }
49428
- __name(printJson, "printJson");
51424
+ __name(printJson3, "printJson");
49429
51425
  var TICK = chalk3.green("\u2713");
49430
51426
  var WARN = chalk3.yellow("!");
49431
51427
  var CROSS = chalk3.red("\u2717");
@@ -49708,7 +51704,7 @@ async function statusCommand(options) {
49708
51704
  hints
49709
51705
  };
49710
51706
  if (options.json) {
49711
- printJson(report2);
51707
+ printJson3(report2);
49712
51708
  } else {
49713
51709
  printHuman(report2);
49714
51710
  }
@@ -53102,31 +55098,58 @@ function runMetering(run) {
53102
55098
  return run.usage?.metering === "priced" ? "priced" : "flat";
53103
55099
  }
53104
55100
  __name(runMetering, "runMetering");
55101
+ var SEAT_COPY = "charged like chat \u2014 actions = the call's price band \xD7 the model's multiplier per model call; cached tokens count at the provider's ratio";
53105
55102
  function budgetCopy(run) {
53106
55103
  if (runMetering(run) === "priced") {
53107
- if (run.usage?.engine === "seat") return "charged like chat \u2014 actions (tier \xD7 model multiplier) per model call on your seat plan";
53108
- if (run.usage?.engine === "legacy") return "charged like chat \u2014 one credit per model call on the legacy plan";
53109
- return "charged like chat \u2014 one credit per model call on the legacy plan, or actions (tier \xD7 model multiplier) on a seat plan";
55104
+ const engine = budgetEngineOf(run);
55105
+ if (engine === "seat") return SEAT_COPY;
55106
+ if (engine === "legacy") return "charged like chat \u2014 a credit is one model reply on a Job step, one agent step otherwise; tokens are never metered";
55107
+ return "charged like chat \u2014 a credit is one model reply on a Job step (one agent step otherwise) on the legacy plan, or actions = price band \xD7 the model's multiplier per model call on a seat plan";
53110
55108
  }
55109
+ if (run.budget?.unit === "actions") return SEAT_COPY;
53111
55110
  return "a credit is one agent step; a Job-tier attempt is 4 \u2014 tokens are never metered";
53112
55111
  }
53113
55112
  __name(budgetCopy, "budgetCopy");
55113
+ function budgetEngineOf(run) {
55114
+ const engine = run.usage?.engine;
55115
+ if (engine === "seat" || engine === "legacy") return engine;
55116
+ const unit = run.budget?.unit;
55117
+ return unit === "actions" ? "seat" : unit === "credits" ? "legacy" : void 0;
55118
+ }
55119
+ __name(budgetEngineOf, "budgetEngineOf");
53114
55120
  var fmtNum = /* @__PURE__ */ __name((v) => v.toLocaleString("en-US"), "fmtNum");
53115
55121
  function budgetLine(run) {
53116
55122
  const u = run.usage;
53117
55123
  const b = run.budget;
53118
55124
  if (!u && !b) return void 0;
53119
- const seat = u?.engine === "seat" || b?.unit === "actions";
55125
+ const seat = budgetEngineOf(run) === "seat";
53120
55126
  const spent = seat ? u?.actionsUsed ?? b?.spent?.actionsEstimate ?? 0 : u?.creditsUsed ?? b?.spent?.credits ?? 0;
53121
55127
  const cap = runBudgetCap(b);
53122
55128
  const parts = [
53123
55129
  `${fmtNum(spent)}${cap !== void 0 ? ` of ${fmtNum(cap)}` : ""} ${seat ? "actions" : "credits"} spent`,
53124
55130
  b?.remaining !== void 0 ? `${fmtNum(b.remaining)} remaining` : void 0,
53125
- seat && u?.creditsUsed ? `${fmtNum(u.creditsUsed)} flat credits` : void 0
55131
+ seat && u?.creditsUsed ? `${fmtNum(u.creditsUsed)} flat credits` : void 0,
55132
+ // [WJB-B5] (BAC-677 / plan D4): the run's own `budget.exceeded` stamp — the last step crossed the cap and
55133
+ // the run finished rather than parking for a raise nobody could act on. Printed only when the server
55134
+ // stamped it (never `false`, never inferred from `spent > cap`, which a concurrent settle can show for a
55135
+ // run that parked legitimately).
55136
+ b?.exceeded === true ? "finished past the cap" : void 0
53126
55137
  ].filter((p) => !!p);
53127
55138
  return `${parts.join(" \xB7 ")} (${budgetCopy(run)})`;
53128
55139
  }
53129
55140
  __name(budgetLine, "budgetLine");
55141
+ function tokensLine(run) {
55142
+ const t = run.usage?.tokens;
55143
+ if (!t) return void 0;
55144
+ const parts = [
55145
+ typeof t.inputTokens === "number" ? `${fmtNum(t.inputTokens)} uncached in` : void 0,
55146
+ typeof t.cacheReadTokens === "number" ? `${fmtNum(t.cacheReadTokens)} cache read` : void 0,
55147
+ typeof t.cacheCreationTokens === "number" ? `${fmtNum(t.cacheCreationTokens)} cache write` : void 0,
55148
+ typeof t.outputTokens === "number" ? `${fmtNum(t.outputTokens)} out` : void 0
55149
+ ].filter((p) => !!p);
55150
+ return parts.length ? parts.join(" \xB7 ") : void 0;
55151
+ }
55152
+ __name(tokensLine, "tokensLine");
53130
55153
  function autonomyDurationLabel(seconds) {
53131
55154
  if (!Number.isFinite(seconds) || seconds < 0) return `${seconds} s`;
53132
55155
  if (seconds >= 86400 && seconds % 86400 === 0) return `${fmtNum(seconds / 86400)} d`;
@@ -53265,6 +55288,8 @@ function printRun(run, withSteps) {
53265
55288
  if (consent) console.log(` Consent: ${consent}`);
53266
55289
  const budget = budgetLine(run);
53267
55290
  if (budget) console.log(` Budget: ${budget}`);
55291
+ const tokens = tokensLine(run);
55292
+ if (tokens) console.log(` Tokens: ${tokens}`);
53268
55293
  if (run.cancel) console.log(` Cancel: ${formatRunCancel(run.cancel)}`);
53269
55294
  if (run.gate) {
53270
55295
  const kind = run.gate.kind;
@@ -53275,7 +55300,7 @@ function printRun(run, withSteps) {
53275
55300
  } else if (kind === "budget") {
53276
55301
  console.log(`
53277
55302
  \u23F8\uFE0F Paused \u2014 run budget reached (${budgetCopy(run)}):`);
53278
- console.log(` lua workflows raise-budget ${id} --credits <n>`);
55303
+ console.log(` lua workflows raise-budget ${id} --credits <n> (<n> is the new cap in ${budgetEngineOf(run) === "seat" ? "actions" : "credits"})`);
53279
55304
  } else if (kind === "billing") {
53280
55305
  for (const line of billingParkLines(run)) console.log(line);
53281
55306
  } else if (kind === "model_policy") {
@@ -53309,11 +55334,17 @@ function printRun(run, withSteps) {
53309
55334
  else {
53310
55335
  console.log("");
53311
55336
  const withChildren = steps.some((s) => typeof s.childRunId === "string");
55337
+ const withTokens = steps.some((s) => !!s.tokens);
53312
55338
  console.log(renderTable([
53313
55339
  "Step",
53314
55340
  "Kind",
53315
55341
  "Status",
53316
55342
  "Attempt",
55343
+ ...withTokens ? [
55344
+ "Uncached",
55345
+ "Cached",
55346
+ "Output"
55347
+ ] : [],
53317
55348
  "Error",
53318
55349
  ...withChildren ? [
53319
55350
  "Child run"
@@ -53323,6 +55354,7 @@ function printRun(run, withSteps) {
53323
55354
  s.kind ?? "\u2014",
53324
55355
  s.status,
53325
55356
  stepAttemptCell(s),
55357
+ ...withTokens ? stepTokenCells(s) : [],
53326
55358
  stepErrorCell(s.error),
53327
55359
  ...withChildren ? [
53328
55360
  s.childRunId ?? ""
@@ -53352,6 +55384,22 @@ function billingParkLines(run) {
53352
55384
  return out;
53353
55385
  }
53354
55386
  __name(billingParkLines, "billingParkLines");
55387
+ function stepTokenCells(s) {
55388
+ const t = s.tokens;
55389
+ const cell = /* @__PURE__ */ __name((v) => typeof v === "number" && Number.isFinite(v) ? fmtNum(v) : "\u2014", "cell");
55390
+ if (!t) return [
55391
+ "\u2014",
55392
+ "\u2014",
55393
+ "\u2014"
55394
+ ];
55395
+ const cached = typeof t.cacheReadTokens === "number" || typeof t.cacheCreationTokens === "number" ? (t.cacheReadTokens ?? 0) + (t.cacheCreationTokens ?? 0) : void 0;
55396
+ return [
55397
+ cell(t.inputTokens),
55398
+ cell(cached),
55399
+ cell(t.outputTokens)
55400
+ ];
55401
+ }
55402
+ __name(stepTokenCells, "stepTokenCells");
53355
55403
  function stepAttemptLines(s) {
53356
55404
  const out = [];
53357
55405
  const cause = stepFailureLine(s.error);
@@ -53360,6 +55408,8 @@ function stepAttemptLines(s) {
53360
55408
  if (burned) out.push(` \u03A3 ${s.stepId}: attempt ${s.attempt ?? 1} ${s.status}: ${burned}`);
53361
55409
  const billed = stepBillingLine(s.billing);
53362
55410
  if (billed) out.push(` $ ${s.stepId}: attempt ${s.billing?.attempt ?? s.attempt ?? 1} billed: ${billed}`);
55411
+ const jobModel = stepJobModelLine(s);
55412
+ if (jobModel) out.push(` \u2699 ${s.stepId}: ${jobModel}`);
53363
55413
  const fetchable = outputRefLine(s.outputRef);
53364
55414
  if (fetchable) out.push(` \u21E9 ${s.stepId}: ${fetchable}`);
53365
55415
  const escalated = approverFallbackLine(s);
@@ -53373,6 +55423,22 @@ function stepAttemptLines(s) {
53373
55423
  return out;
53374
55424
  }
53375
55425
  __name(stepAttemptLines, "stepAttemptLines");
55426
+ function stepJobModelLine(s) {
55427
+ const j = s.job;
55428
+ if (!j) return void 0;
55429
+ const parts = [
55430
+ j.model ? `model ${j.model}` : void 0,
55431
+ typeof j.multiplier === "number" ? `\xD7${fmtNum(j.multiplier)}` : void 0,
55432
+ j.modelSource ? `chosen by ${jobModelSourceWord(j.modelSource)}` : void 0
55433
+ ].filter((p) => !!p);
55434
+ return parts.length ? parts.join(" \xB7 ") : void 0;
55435
+ }
55436
+ __name(stepJobModelLine, "stepJobModelLine");
55437
+ function jobModelSourceWord(source) {
55438
+ const labels = WORKFLOW_JOB_MODEL_SOURCE_LABELS;
55439
+ return labels[source] ?? source;
55440
+ }
55441
+ __name(jobModelSourceWord, "jobModelSourceWord");
53376
55442
  function approverFallbackLine(s) {
53377
55443
  const f = s.suspend?.approverFallback;
53378
55444
  if (!f?.from || !f.to) return void 0;
@@ -53674,18 +55740,23 @@ __name(handleWatchFrame, "handleWatchFrame");
53674
55740
  async function boundaryVerdict(runId, ev, state3, boundary) {
53675
55741
  const type = ev.type;
53676
55742
  if (type === "run.gated") {
53677
- const gate = ev.data?.gate;
53678
- const stepId = ev.data?.stepId;
53679
- if (gate === "exception") {
55743
+ const raw = ev.data;
55744
+ const rawKind = raw?.kind ?? raw?.gate;
55745
+ const stepId = raw?.stepId;
55746
+ if (rawKind === "exception") {
53680
55747
  return boundary(`\u26A0\uFE0F run parked on an exception gate \u2014 a human decides next: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
53681
55748
  }
53682
- if (gate === "billing") {
55749
+ if (rawKind === "billing") {
53683
55750
  return boundary(`\u23F8\uFE0F run parked on a billing gate \u2014 top up the workspace; it resumes within a few minutes (right away: lua workflows retry-step ${runId} --step ${stepId ?? "<stepId>"})`, WORKFLOW_EXIT.RUN_PARKED);
53684
55751
  }
53685
- if (gate === "budget") {
53686
- return boundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
55752
+ if (rawKind === "budget") {
55753
+ return boundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (the cap is in this run's own budget unit \u2014 credits, or actions on a seat plan; lua workflows status ${runId} says which)`, WORKFLOW_EXIT.RUN_PARKED);
53687
55754
  }
53688
- return boundary(`\u23F8\uFE0F run gated (${gate ?? "consent"}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
55755
+ if (rawKind === "quota") {
55756
+ return boundary(`\u23F8\uFE0F run gated on the concurrency quota \u2014 it clears on its own as a slot frees (oldest-gated run first); check progress: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_GATED);
55757
+ }
55758
+ const label2 = rawKind === "start-consent" || rawKind === void 0 ? "consent" : rawKind;
55759
+ return boundary(`\u23F8\uFE0F run gated (${label2}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
53689
55760
  }
53690
55761
  if (type === "run.suspended") {
53691
55762
  const kind = ev.data?.kind;
@@ -53700,7 +55771,7 @@ async function boundaryVerdict(runId, ev, state3, boundary) {
53700
55771
  }
53701
55772
  }
53702
55773
  if (type === "run.budget_parked") {
53703
- return boundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
55774
+ return boundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (the cap is in this run's own budget unit \u2014 credits, or actions on a seat plan; lua workflows status ${runId} says which)`, WORKFLOW_EXIT.RUN_PARKED);
53704
55775
  }
53705
55776
  if (type === "run.completed") return WORKFLOW_EXIT.OK;
53706
55777
  if (type === "run.failed" || type === "run.timed_out") return WORKFLOW_EXIT.RUN_FAILED;
@@ -53930,10 +56001,10 @@ function budgetCapFlag(cap) {
53930
56001
  return "--credits";
53931
56002
  }
53932
56003
  __name(budgetCapFlag, "budgetCapFlag");
53933
- function budgetCapsLabel(b) {
56004
+ function budgetCapsLabel(b, unit) {
53934
56005
  if (!b) return "";
53935
56006
  const parts = [];
53936
- if (b.maxCredits !== void 0) parts.push(`${b.maxCredits} credits`);
56007
+ if (b.maxCredits !== void 0) parts.push(`${b.maxCredits} ${unit ?? "credits"}`);
53937
56008
  if (b.maxSteps !== void 0) parts.push(`${b.maxSteps} steps`);
53938
56009
  if (b.maxJobSeconds !== void 0) parts.push(`${b.maxJobSeconds}s job`);
53939
56010
  if (b.maxDurationSeconds !== void 0) parts.push(`${b.maxDurationSeconds}s wall`);
@@ -53999,9 +56070,10 @@ async function raiseBudgetCore(ctx, runId, o) {
53999
56070
  emitJson(ctx, res);
54000
56071
  if (!ctx.json) {
54001
56072
  const d = res.data;
54002
- const caps = budgetCapsLabel(d.budget ?? d.to);
56073
+ const unit = d.unit === "actions" ? "actions" : "credits";
56074
+ const caps = budgetCapsLabel(d.budget ?? d.to, unit);
54003
56075
  if (d.raised) {
54004
- const delta = d.from && d.to ? ` (${budgetCapsLabel(d.from)} \u2192 ${budgetCapsLabel(d.to)})` : caps ? ` to ${caps}` : "";
56076
+ const delta = d.from && d.to ? ` (${budgetCapsLabel(d.from, unit)} \u2192 ${budgetCapsLabel(d.to, unit)})` : caps ? ` to ${caps}` : "";
54005
56077
  writeSuccess(`\u2705 budget raised${delta}${d.runStatus ? ` \xB7 run is ${d.runStatus}` : ""}`);
54006
56078
  if (d.resumed === false) writeInfo(`\u2139\uFE0F the run did not resume on this raise \u2014 the next step's reserve is still short (raise again), or nothing was parked: lua workflows status ${runId}`);
54007
56079
  else if (d.resumed) writeInfo(`\u{1F4A1} Follow it: lua workflows watch ${runId}`);
@@ -54346,23 +56418,77 @@ async function replayChildRun(ctx, childRunId) {
54346
56418
  };
54347
56419
  }
54348
56420
  __name(replayChildRun, "replayChildRun");
54349
- var WORKFLOW_LOG_EVENT_PATTERN = /progress|log|started|completed|failed|skipped|model_resolved|model_policy_warning/;
56421
+ var WORKFLOW_LOG_EVENT_PATTERN = /progress|log|started|completed|failed|skipped|model_resolved|model_policy_warning|budget_parked|budget_raised|budget_exceeded/;
56422
+ function jobModelEventDetail(data) {
56423
+ const d = data ?? {};
56424
+ const nested = d.detail && typeof d.detail === "object" ? d.detail : {};
56425
+ const pick = /* @__PURE__ */ __name((key) => d[key] !== void 0 ? d[key] : nested[key], "pick");
56426
+ const model = typeof pick("model") === "string" ? pick("model") : void 0;
56427
+ const raw = pick("multiplier");
56428
+ const multiplier = typeof raw === "number" && Number.isFinite(raw) ? raw : void 0;
56429
+ const source = typeof pick("source") === "string" ? pick("source") : void 0;
56430
+ const calibrated = pick("calibrated");
56431
+ if (!model && multiplier === void 0 && !source) return void 0;
56432
+ const tail = [
56433
+ multiplier !== void 0 ? `\xD7${fmtNum(multiplier)}` : void 0,
56434
+ source ? `(${jobModelSourceWord(source)})` : void 0,
56435
+ calibrated === false ? "\u2014 uncalibrated model, fallback rate" : void 0
56436
+ ].filter((p) => !!p);
56437
+ return `job model \u2192 ${model ?? "?"}${tail.length ? ` ${tail.join(" ")}` : ""}`;
56438
+ }
56439
+ __name(jobModelEventDetail, "jobModelEventDetail");
56440
+ function budgetEventDetail(type, data) {
56441
+ if (type !== "run.budget_parked" && type !== "run.budget_raised" && type !== "run.budget_exceeded") return void 0;
56442
+ const d = data ?? {};
56443
+ const nested = d.detail && typeof d.detail === "object" ? d.detail : {};
56444
+ const pick = /* @__PURE__ */ __name((key) => d[key] !== void 0 ? d[key] : nested[key], "pick");
56445
+ const numberOf = /* @__PURE__ */ __name((key) => {
56446
+ const v = pick(key);
56447
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
56448
+ }, "numberOf");
56449
+ const stringOf = /* @__PURE__ */ __name((key) => {
56450
+ const v = pick(key);
56451
+ return typeof v === "string" && v ? v : void 0;
56452
+ }, "stringOf");
56453
+ const stepId = stringOf("stepId");
56454
+ const step3 = stepId ? ` (step ${stepId})` : "";
56455
+ if (type === "run.budget_raised") {
56456
+ const from = numberOf("from");
56457
+ const to = numberOf("to");
56458
+ const move = from !== void 0 && to !== void 0 ? `cap ${fmtNum(from)} \u2192 ${fmtNum(to)}` : "cap raised";
56459
+ const by = stringOf("by");
56460
+ const resumed = pick("resumed") === true ? " \u2014 run resumed" : "";
56461
+ return `${move}${by ? ` by ${by}` : ""}${resumed}`;
56462
+ }
56463
+ const dimension = stringOf("dimension");
56464
+ const spent = numberOf("spent");
56465
+ const cap = numberOf("cap");
56466
+ const figures = spent !== void 0 && cap !== void 0 ? `${fmtNum(spent)} of ${fmtNum(cap)}${dimension ? ` ${dimension}` : ""}` : dimension;
56467
+ const lead = type === "run.budget_exceeded" ? "finished past the cap" : "paused on its run budget";
56468
+ const detail = [
56469
+ figures,
56470
+ stringOf("engine")
56471
+ ].filter((p) => !!p).join(", ");
56472
+ return `${lead}${detail ? ` \u2014 ${detail}` : ""}${step3}`;
56473
+ }
56474
+ __name(budgetEventDetail, "budgetEventDetail");
54350
56475
  function modelEventDetail(type, data) {
56476
+ if (type === "step.job_model_resolved") return jobModelEventDetail(data);
54351
56477
  if (type !== "step.model_resolved" && type !== "step.model_policy_warning") return void 0;
54352
56478
  const d = data ?? {};
54353
56479
  const nested = d.detail ?? {};
54354
- const str = /* @__PURE__ */ __name((...vals) => {
56480
+ const str2 = /* @__PURE__ */ __name((...vals) => {
54355
56481
  for (const v of vals) if (typeof v === "string" && v) return v;
54356
56482
  return void 0;
54357
56483
  }, "str");
54358
- const requested = str(d.requested, nested.requested, d.modelRequested, nested.modelRequested);
54359
- const resolved = str(d.resolved, nested.resolved, d.model, nested.model);
56484
+ const requested = str2(d.requested, nested.requested, d.modelRequested, nested.modelRequested);
56485
+ const resolved = str2(d.resolved, nested.resolved, d.model, nested.model);
54360
56486
  if (type === "step.model_resolved") {
54361
- const cls = requested?.startsWith("class/") ? requested.slice("class/".length) : str(d.taskClass, nested.taskClass);
56487
+ const cls = requested?.startsWith("class/") ? requested.slice("class/".length) : str2(d.taskClass, nested.taskClass);
54362
56488
  return `model resolved \u2192 ${resolved ?? requested ?? "?"}${cls ? ` (${cls})` : ""}`;
54363
56489
  }
54364
- const outcome = str(d.outcome, nested.outcome, d.code, nested.code) ?? "unknown";
54365
- const detail = str(d.message, nested.message) ?? (requested && resolved && requested !== resolved ? `${requested} \u2192 ${resolved}` : requested ?? resolved);
56490
+ const outcome = str2(d.outcome, nested.outcome, d.code, nested.code) ?? "unknown";
56491
+ const detail = str2(d.message, nested.message) ?? (requested && resolved && requested !== resolved ? `${requested} \u2192 ${resolved}` : requested ?? resolved);
54366
56492
  return `model policy warning: ${outcome}${detail ? ` ${detail}` : ""}`;
54367
56493
  }
54368
56494
  __name(modelEventDetail, "modelEventDetail");
@@ -54378,7 +56504,9 @@ async function logsCore(ctx, runId, o) {
54378
56504
  if (events.length === 0) console.log("(no log events yet)");
54379
56505
  for (const e of events) {
54380
56506
  const data = scrubEventData(e.data);
54381
- const msg = modelEventDetail(e.type, data) ?? data?.message ?? subrunEventDetail(e);
56507
+ const msg = modelEventDetail(e.type, data) ?? // [WJB-B5]: the budget events carry no `message` of their own — without a renderer they would print as a
56508
+ // bare type, which is why the pattern admitting them and this line land together.
56509
+ budgetEventDetail(e.type, data) ?? data?.message ?? subrunEventDetail(e);
54382
56510
  console.log(`[${clockOf(e.ts)}] ${e.type}${e.stepId ? ` \xB7 ${e.stepId}` : ""}${msg ? ` \xB7 ${msg}` : ""}`);
54383
56511
  }
54384
56512
  return WORKFLOW_EXIT.OK;
@@ -54756,7 +56884,7 @@ async function archiveRunsCore(ctx, o) {
54756
56884
  if (!ctx.json) console.log(line);
54757
56885
  }, "say");
54758
56886
  const sha256 = /* @__PURE__ */ __name((buf) => crypto5.createHash("sha256").update(buf).digest("hex"), "sha256");
54759
- const sleep3 = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "sleep");
56887
+ const sleep4 = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "sleep");
54760
56888
  const archiveOne = /* @__PURE__ */ __name(async (apiRow) => {
54761
56889
  const run = {
54762
56890
  ...apiRow,
@@ -54814,7 +56942,7 @@ async function archiveRunsCore(ctx, o) {
54814
56942
  }
54815
56943
  let dto = req.data;
54816
56944
  for (let attempt = 0; !dto || dto.status === "pending"; attempt++) {
54817
- if (attempt > 0) await sleep3(Math.min(5e3, 250 * 2 ** Math.min(attempt, 5)));
56945
+ if (attempt > 0) await sleep4(Math.min(5e3, 250 * 2 ** Math.min(attempt, 5)));
54818
56946
  if (attempt > 60) {
54819
56947
  report2.deferred.push({
54820
56948
  runId: run.id,
@@ -56315,12 +58443,8 @@ async function clearGateCore(ctx, runId, o) {
56315
58443
  if (!res.success || !res.data) return apiFailure(ctx, res, "clear-gate");
56316
58444
  emitJson(ctx, res);
56317
58445
  if (ctx.json) return WORKFLOW_EXIT.OK;
56318
- if (res.data.cleared) {
56319
- writeSuccess(`\u2705 Cleared the ${res.data.kind ?? kind} gate on ${runId}${res.data.status ? ` \u2014 ${res.data.status}` : ""}`);
56320
- console.log(` lua workflows watch ${runId}`);
56321
- } else {
56322
- writeInfo(`\u2139\uFE0F ${runId} is not parked on a ${kind} gate${res.data.reason ? ` \u2014 ${res.data.reason}` : ""}. Nothing to clear.`);
56323
- }
58446
+ writeSuccess(`\u2705 Cleared the ${res.data.kind ?? kind} gate on ${runId}${res.data.status ? ` \u2014 ${res.data.status}` : ""}`);
58447
+ console.log(` lua workflows watch ${runId}`);
56324
58448
  return WORKFLOW_EXIT.OK;
56325
58449
  }
56326
58450
  __name(clearGateCore, "clearGateCore");
@@ -56345,12 +58469,18 @@ async function recomposeCore(ctx, name, _o) {
56345
58469
  emitJson(ctx, res);
56346
58470
  if (ctx.json) return WORKFLOW_EXIT.OK;
56347
58471
  const d = res.data;
56348
- if (d.unchanged) {
58472
+ const rewrittenCount = d.rewritten?.length ?? 0;
58473
+ if (rewrittenCount === 0) {
56349
58474
  writeInfo(`\u2139\uFE0F ${wf.name} already asks for model classes everywhere it can \u2014 no new version was published.`);
56350
58475
  return WORKFLOW_EXIT.OK;
56351
58476
  }
56352
- writeSuccess(`\u2705 ${wf.name} recomposed \u2192 version ${d.version ?? d.versionId ?? "(new)"}` + (d.rewritten !== void 0 ? ` \xB7 ${d.rewritten} agent step${d.rewritten === 1 ? "" : "s"} rewritten` : ""));
56353
- for (const line of recomposeNextSteps(wf, d.version)) console.log(line);
58477
+ const saved = d.composed?.saved;
58478
+ const version = saved?.version ?? saved?.versionId ?? "(new)";
58479
+ writeSuccess(`\u2705 ${wf.name} recomposed \u2192 version ${version} \xB7 ${rewrittenCount} agent step${rewrittenCount === 1 ? "" : "s"} rewritten`);
58480
+ if (saved?.supersededBy) {
58481
+ writeInfo(`\u2139\uFE0F a concurrent recompose already holds the pin at version ${saved.supersededBy} \u2014 that is what runs.`);
58482
+ }
58483
+ for (const line of recomposeNextSteps(wf, version)) console.log(line);
56354
58484
  return WORKFLOW_EXIT.OK;
56355
58485
  }
56356
58486
  __name(recomposeCore, "recomposeCore");
@@ -58395,10 +60525,10 @@ async function templateCommand(action, options = {}) {
58395
60525
  });
58396
60526
  }
58397
60527
  __name(templateCommand, "templateCommand");
58398
- function sleep2(ms) {
60528
+ function sleep3(ms) {
58399
60529
  return new Promise((resolve7) => setTimeout(resolve7, ms));
58400
60530
  }
58401
- __name(sleep2, "sleep");
60531
+ __name(sleep3, "sleep");
58402
60532
  function missingVersionOrRethrow(error) {
58403
60533
  if (CliError.isCliError(error) && error.statusCode === 404) return null;
58404
60534
  throw error;
@@ -58442,12 +60572,12 @@ function parseEnvContract(raw) {
58442
60572
  const result = {};
58443
60573
  for (const entry of raw) {
58444
60574
  for (const pair of entry.split(/,(?=\s*[A-Za-z_][A-Za-z0-9_]*\??\s*=)/)) {
58445
- const trimmed = pair.trim();
58446
- if (!trimmed) continue;
58447
- const eqIdx = trimmed.indexOf("=");
60575
+ const trimmed2 = pair.trim();
60576
+ if (!trimmed2) continue;
60577
+ const eqIdx = trimmed2.indexOf("=");
58448
60578
  if (eqIdx === -1) continue;
58449
- let key = trimmed.slice(0, eqIdx).trim();
58450
- const description = trimmed.slice(eqIdx + 1).trim();
60579
+ let key = trimmed2.slice(0, eqIdx).trim();
60580
+ const description = trimmed2.slice(eqIdx + 1).trim();
58451
60581
  let required = true;
58452
60582
  if (key.endsWith("?")) {
58453
60583
  required = false;
@@ -59014,8 +61144,7 @@ Apply v${versionObj.version} to ${targets.length} agent(s)?`);
59014
61144
  writeProgress("\u{1F504} Starting apply run...");
59015
61145
  const { runId } = await templateApi.apply(templateId, {
59016
61146
  version: versionObj.version,
59017
- targets,
59018
- skipEnvCheck: options.skipEnvCheck
61147
+ targets
59019
61148
  });
59020
61149
  if (options.wait === false) {
59021
61150
  if (options.json) {
@@ -59034,7 +61163,7 @@ Apply v${versionObj.version} to ${targets.length} agent(s)?`);
59034
61163
  if (Date.now() > waitDeadline) {
59035
61164
  throw new Error(`Apply run ${runId} still running after 30 minutes \u2014 a crashed worker can leave a run stuck. Check later with: lua marketplace template status --template-id ${templateId}`);
59036
61165
  }
59037
- await sleep2(3e3);
61166
+ await sleep3(3e3);
59038
61167
  run = await templateApi.getApplyRun(templateId, runId);
59039
61168
  }
59040
61169
  if (options.json) {
@@ -67422,7 +69551,7 @@ function setupMarketplaceCommands(program2) {
67422
69551
  program2.command("marketplace [noun] [action]").description("\u{1F6CD}\uFE0F Browse, install, and manage marketplace skills and agent templates").option("--skill-name <name>", "Skill name (for skill list/update/uninstall)").option("--marketplace-id <id>", "Marketplace skill ID").option("--new-org-id <id>", "Destination organization ID (for skill transfer)").option("--version-id <id>", "Version ID (for skill publish/install)").option("--env-vars-json <json>", "JSON string with env var metadata (for skill publish)").option("--query <text>", "Search query (for skill search)").option("--page <n>", "Page number (for skill search)").option("--limit <n>", "Results per page (for skill search)").option("--name <name>", "Template name, an internal identifier (for template create)").option("--description <text>", "Description (for template create)").option("--template-id <id>", "Template ID").option("--window-days <n>", "Fleet-health window in days (template health, R46)").option("--source-version <n>", "Agent version to freeze into this template version (default: active) (for template publish)").option("--env-contract <pair>", "KEY=description env contract entry, repeatable; use KEY?=description for optional (for template publish)", (val, previous) => [
67423
69552
  ...previous,
67424
69553
  val
67425
- ], []).option("--skip-auto-apply", "Publish without auto-updating the installs that consented to creator updates (for template publish)").option("--version <n>", "Template version (for template view/install/apply)").option("--allow-creator-updates", "Allow the template creator to push future updates onto this install").option("--skip-env-check", "Skip env-contract validation (for template install/apply)").option("--agents <ids>", "Comma-separated target agent IDs (for template apply)").option("--file <path>", "Path to a file with one target agent ID per line (for template apply)").option("--all-installed", "Target every agent that already has this template installed (for template apply)").option("--no-wait", "Print the apply runId immediately instead of polling for completion (for template apply)").option("--display-name <name>", "Display name (for skill list/edit, template create)").option("--visibility <visibility>", "Who can see and install it: public or private (for skill list, template create)").option("--changelog <text>", "Changelog for this version (for skill/template publish)").option("--env-vars <pairs>", "Comma-separated key=value pairs (for skill/template install/update)").option("--json", "Output as JSON").option("--force", "Skip confirmation prompts").option("--yes", "Auto-confirm ONLY the template-publish consequence prompt (section clear/narrow); unlike --force it skips no other confirmation").addHelpText("after", `
69554
+ ], []).option("--skip-auto-apply", "Publish without auto-updating the installs that consented to creator updates (for template publish)").option("--version <n>", "Template version (for template view/install/apply)").option("--allow-creator-updates", "Allow the template creator to push future updates onto this install").option("--skip-env-check", "Deprecated no-op: the env-contract check is server-enforced; use `lua env` to satisfy the contract (for template install/apply)").option("--agents <ids>", "Comma-separated target agent IDs (for template apply)").option("--file <path>", "Path to a file with one target agent ID per line (for template apply)").option("--all-installed", "Target every agent that already has this template installed (for template apply)").option("--no-wait", "Print the apply runId immediately instead of polling for completion (for template apply)").option("--display-name <name>", "Display name (for skill list/edit, template create)").option("--visibility <visibility>", "Who can see and install it: public or private (for skill list, template create)").option("--changelog <text>", "Changelog for this version (for skill/template publish)").option("--env-vars <pairs>", "Comma-separated key=value pairs (for skill/template install/update)").option("--json", "Output as JSON").option("--force", "Skip confirmation prompts").option("--yes", "Auto-confirm ONLY the template-publish consequence prompt (section clear/narrow); unlike --force it skips no other confirmation").addHelpText("after", `
67426
69555
  Arguments:
67427
69556
  noun Optional: 'skill' or 'template' (prompts if not provided)
67428
69557
  action Optional: specific action for non-interactive mode
@@ -67699,9 +69828,9 @@ Examples:
67699
69828
  $ lua channels Interactive management
67700
69829
  $ lua channels list List all channels
67701
69830
  `).action(channelsCommand);
67702
- program2.command("logs").description("\u{1F4CA} View agent and skill logs").option("--type <type>", "Filter by type: all, skill, job, webhook, preprocessor, postprocessor, user_message, agent_response, mcp, mastra").option("--name <name>", "Entity name to filter (requires --type)").option("--user-id <userId>", "Filter logs by user ID").option("--agent-id <agentId>", "View logs for a specific agent ID (admin access required)").option("--limit <n>", "Number of logs to show (default: 20)").option("--page <n>", "Page number for pagination").option("--json", "Output as JSON (for scripting)").option("--direction <direction>", "Filter calls by direction (inbound | outbound) \u2014 only with --type calls").option("--status <status>", "Filter calls by status \u2014 only with --type calls").addHelpText("after", `
69831
+ program2.command("logs").description("\u{1F4CA} View agent and skill logs").option("--type <type>", "Filter by type: all, calls, or any of the 18 log sources (skill, job, webhook, trigger, preprocessor, postprocessor, user_message, agent_response, agent_error, runtime, mcp, rag, device, device-trigger, model-resolver, workflow-step, workflow-script, workflow)").option("--name <name>", "Entity name to filter (requires --type)").option("--user-id <userId>", "Filter logs by user ID").option("--agent-id <agentId>", "View logs for a specific agent ID (admin access required)").option("--limit <n>", "Number of logs to show (default: 20)").option("--page <n>", "Page number for pagination").option("--json", "Output as JSON (for scripting)").option("--direction <direction>", "Filter calls by direction (inbound | outbound) \u2014 only with --type calls").option("--status <status>", "Filter calls by status \u2014 only with --type calls").option("--since <when>", "Only logs at or after this time: an ISO 8601 instant, or 15m | 2h | 7d").option("--until <when>", "Only logs at or before this time; same formats (ends a --follow)").option("--environment <env>", "production | sandbox (a row with no environment reads as production)").option("--follow", "Stream new logs as they arrive (Ctrl-C to stop); not valid with --page").addHelpText("after", `
67703
69832
  Options:
67704
- --type <type> Filter by type: all, skill, job, webhook, preprocessor, postprocessor, user_message, agent_response, mcp, mastra, calls
69833
+ --type <type> Filter by type: all, calls, or any of the 18 log sources (skill, job, webhook, trigger, preprocessor, postprocessor, user_message, agent_response, agent_error, runtime, mcp, rag, device, device-trigger, model-resolver, workflow-step, workflow-script, workflow)
67705
69834
  --name <name> Entity name to filter (requires --type, not for message types)
67706
69835
  --user-id <userId> Filter logs by user ID
67707
69836
  --agent-id <agentId> View logs for a specific agent ID (admin access required, overrides the agent in lua.config.yaml)
@@ -67710,6 +69839,11 @@ Options:
67710
69839
  --json Output as JSON for scripting
67711
69840
  --direction <dir> Filter calls by direction (inbound | outbound) \u2014 only with --type calls
67712
69841
  --status <status> Filter calls by status \u2014 only with --type calls
69842
+ --since <when> Inclusive lower bound: an ISO 8601 instant, or a relative window (15m, 2h, 7d)
69843
+ --until <when> Inclusive upper bound, same formats; it also ends a --follow
69844
+ --environment <env> production | sandbox \u2014 a row with no environment reads as production
69845
+ --follow Poll for new logs until Ctrl-C; refuses --page, and --json emits one
69846
+ {logs, nextCursor, pagination} envelope per poll
67713
69847
 
67714
69848
  Examples:
67715
69849
  $ lua logs Interactive selection
@@ -67723,6 +69857,16 @@ Examples:
67723
69857
  $ lua logs --type calls Recent voice calls (renders as table)
67724
69858
  $ lua logs --type calls --direction outbound Outbound calls only
67725
69859
  $ lua logs --type calls --status failed --json Failed calls as JSON
69860
+ $ lua logs --since 15m The last fifteen minutes
69861
+ $ lua logs --since 2026-09-21T09:00:00Z --until 2026-09-21T10:00:00Z One hour, exactly
69862
+ $ lua logs --environment sandbox --type skill Sandbox skill logs only
69863
+ $ lua logs --follow Stream new logs until Ctrl-C
69864
+ $ lua logs --follow --since 5m --json Tail the last five minutes, as JSON
69865
+
69866
+ Notes:
69867
+ A relative --since/--until is resolved by the SERVER's clock, so a skewed machine cannot
69868
+ shift the window. --type calls reads voice calls and ignores --since/--until/--environment
69869
+ /--follow. --since is INCLUSIVE; the boundary row is de-duplicated by id while following.
67726
69870
  `).action(logsCommand);
67727
69871
  program2.command("skills [actionOrEnv] [action]").description("\u2699\uFE0F Manage and view skills").option("--skill-name <name>", "Skill name").option("--skill-version <version>", "Version for deploy action (or 'latest')").addHelpText("after", `
67728
69872
  Arguments:
@@ -67765,6 +69909,28 @@ Examples:
67765
69909
  $ lua webhooks subscribe --webhook-name myWebhook --event message.delivered Subscribe to event
67766
69910
  $ lua webhooks unsubscribe --webhook-name myWebhook --event message.delivered Unsubscribe from event
67767
69911
  `).action(webhooksCommand);
69912
+ program2.command("drains [action] [id]").description("\u{1F6B0} Manage log drains (stream this organization's logs to your own destination)").option("--json", "Output as JSON").option("--yes", "Skip confirmation prompts (required with --include-content in CI)").option("--org <id>", "The organization to act on (default: the project, or the key when it reaches one org)").option("--name <name>", "Drain name (1-64 chars, unique per org)").option("--type <type>", "http | otlp | datadog | betterstack").option("--endpoint <url>", "Destination URL (http and otlp only; https, public host)").option("--site <site>", "Preset site/region, e.g. datadoghq.eu").option("--format <format>", "json | ndjson").option("--header <name>", "Custom header NAME (the value is prompted)", collect, []).option("--header-from-env <name=ENV_VAR>", "Custom header, value read from an env var (CI form)", collect, []).option("--sources <list>", "Comma-separated source selectors (default: the recommended set)").option("--min-severity <level>", "debug | info | warn | error (default info)").option("--agents <list>", "'all' or a comma-separated list of agent ids").option("--environments <list>", "production,sandbox (default production)").option("--sampling <spec>", "Head sampling, e.g. debug=0.1").option("--include-content", "Include user messages and agent responses (prints an acknowledgement)").option("--no-include-content", "update: stop sending message bodies (the drain keeps its sources)").option("--limit <n>", "deliveries: rows to return (max 100)").option("--kind <kind>", "deliveries: batch | test | verify | heartbeat | dropped | truncated").option("--reason <text>", "pause: why").option("--finalize", "rotate-secret: close an open rotation window early").addHelpText("after", `
69913
+ Arguments:
69914
+ action Optional: list (default), status, deliveries, create, update, delete,
69915
+ test, verify, pause, resume, rotate-secret
69916
+ id The drain id, for every action but list, status and create
69917
+
69918
+ Examples:
69919
+ $ lua drains List this org's drains
69920
+ $ lua drains status --json Health + quota (exit 2 if any is failing)
69921
+ $ lua drains create --name prod-siem --type http --endpoint https://logs.example.com/lua
69922
+ $ lua drains verify drn_... Prove you own the endpoint
69923
+ $ lua drains deliveries drn_... --limit 20 --kind batch Recent delivery attempts
69924
+ $ lua drains rotate-secret drn_... New signing secret (shown once)
69925
+ $ lua drains rotate-secret drn_... --finalize Close the rotation window
69926
+ $ lua drains pause drn_... --reason "noisy vendor" Stop delivering (buffering continues)
69927
+ $ lua drains update drn_... --no-include-content Stop sending message bodies
69928
+
69929
+ Notes:
69930
+ Secrets are never flags. --header takes a NAME and prompts for the value; in --ci mode use
69931
+ --header-from-env DD_API_KEY=DATADOG_KEY. The HMAC signing secret is minted by the server and
69932
+ printed exactly once, at create and at rotate-secret.
69933
+ `).action(drainsCommand);
67768
69934
  program2.command("devices [action]").description("\u{1F4E1} Manage connected devices").option("--device-name <name>", "Device name").option("--group <group>", "Filter by device group").option("--payload <json>", "JSON payload for test/test-trigger", "{}").option("--timeout <ms>", "Timeout in milliseconds", "30000").option("--force", "Skip confirmation prompts").addHelpText("after", `
67769
69935
  Arguments:
67770
69936
  action Optional: list, status, enable, disable, remove, test, test-trigger
@@ -67808,7 +69974,7 @@ Examples:
67808
69974
  $ lua jobs versions -i myJob View job versions
67809
69975
  $ lua jobs history -i myJob View execution history
67810
69976
  `).action(jobsCommand);
67811
- program2.command("workflows [action] [target] [extra]").description("\u{1F9ED} Manage workflows and their runs (list, start, watch, cancel, resume, retry-step, resolve-step, raise-budget, approve, signal, replay, export, schedules)").option("-i, --workflow-name <name>", "Workflow name (or id)").option("-r, --run-id <id>", "Run id").option("-v, --workflow-version <ver>", "deploy/start/export/goals create/schedules create: a semver, 'latest' (newest push) or a version id").option("--json", "Print the raw {success,data} envelope").option("--all", "list: include dynamic workflows").option("--input <json|@file>", "start/run/goals create/schedules create: run input").option("--idempotency-key <key>", "start: idempotency key").option("--correlation-key <key>", "start/runs: correlation key").option("--tag <tag>", "start/runs/schedules create: tag (repeatable, \u2264 10)", collect, []).option("--budget-credits <n>", "start/schedules create: run budget (credits)").option("--wait <s>", "start: server long-poll \u2264 55 s").option("--follow", "start/logs: attach watch").option("--status <status>", "runs/goals list: filter by status").option("--workflow <name>", "runs: filter by workflow").option("--limit <n>", "runs/goals list/approval-payload --path: page size").option("--cursor <c>", "runs/goals list/approval-payload --path: page cursor").option("--sort <field>", "runs: -createdAt|createdAt|-durationMs|durationMs").option("--steps", "status: per-step table").option("--strict", "status: exit 4/5 by terminal status, 6 for a gated run").option("--compose <on|off>", "policy models set: may the composer be shown the model menu").option("--max-class <fast|balanced|strong>", "policy models set: ceiling for class requests and the class map").option("--consent-actions <n>", "policy models set: estimated actions above which a composed run shows the card").option("--allow <codes>", "policy models set: comma-separated approved codes to narrow the set to").option("--class-fast <codes>", "policy models set: ordered overrides of the fast class (comma-separated)").option("--class-balanced <codes>", "policy models set: ordered overrides of the balanced class (comma-separated)").option("--class-strong <codes>", "policy models set: ordered overrides of the strong class (comma-separated)").option("--class-map <pairs>", "policy models set: comma-separated <taskClass>=<modelClass> pairs").option("--pins <class-only|allowed>", "policy models set: may a composed definition pin a concrete code").option("--fallback <same-class|same-provider>", "policy models set: where a failed leg may hop to").option("--enabled <on|off>", "policy autonomy set: may a start inside the envelope skip the consent card").option("--max-duration <seconds>", "policy autonomy set: the envelope\u2019s duration bound, in seconds").option("--max-actions <n>", "policy autonomy set: the envelope\u2019s estimated-actions bound").option("--max-runs-per-hour <n>", "policy autonomy set: autonomous starts per hour, per agent").option("--forms <list>", "policy autonomy set: comma-separated forms the envelope admits (graph,script,static)").option("--clear <keys>", "policy autonomy set: comma-separated members to clear back to the platform default").option("--agent <agentId>", "policy autonomy: not supported \u2014 no route writes a per-agent envelope (see the refusal)").option("--kind <kind>", "clear-gate: the gate kind to clear (e.g. model_policy)").option("--after <seq>", "watch: replay from seq").option("--timeout <s>", "watch / start --follow: give up after s seconds while the run is still live (exit 7)").option("--events", "watch: print raw frames").option("--wait-for-human", "watch / start --follow: keep following through a human boundary (approval, input, signal, park) instead of exiting 8").option("--reason <text>", "cancel: reason").option("--step <id>", "resume/retry-step/resolve-step/logs: step id").option("--outcome <skip|complete|fail>", "resolve-step: the decision on the parked step (--action is accepted too)").option("--action <skip|complete|fail>", "resolve-step: alias of --outcome").option("--output <json|@file>", "resolve-step: the output a completed step would have produced (required with --outcome complete)").option("--credits <n>", "raise-budget: the new credit cap of a budget-parked run (above the current one)").option("--max-steps <n>", "raise-budget: the new step cap").option("--max-job-seconds <n>", "raise-budget: the new Job-tier seconds cap").option("--max-duration-seconds <n>", "raise-budget: the new wall-clock cap").option("--data <json|@file>", "resume: resumeData").option("--approval <id>", "approve/approval-payload: approval id").option("--path <array.path>", "approval-payload: page one array of a large payload (--cursor / --limit inside it)").option("--decision <approve|deny>", "approve: decision (default approve)").option("--note <text>", "approve/retry-step/resolve-step/raise-budget/goals close: note").option("--edit <json|@file>", "approve: edited payload (small inline edits)").option("--fingerprint <f>", "approve: payloadFingerprint (mandatory with --edit)").option("--payload <json|@file>", "signal: payload").option("--dedupe-key <key>", "signal: dedupe key").option("--local", "replay: replay locally against the compiled artifact").option("--since <dur>", "logs: window").option("--yes", "delete/delete-run/schedules delete: skip confirmation").option("--force", "delete: cancel in-flight runs first \xB7 run: seed from a changed graph \xB7 export: overwrite files").option("--step-output <id=json>", "run: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "run: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "run: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "run: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "run: seed completed steps from a real run").option("--record <dir>", "run: record agent/tool outputs as fixtures").option("--fixtures <dir>", "run: replay recorded fixtures").option("--step-wall <s>", "run: per-step wall in seconds (default 600)").option("--job-wall <s>", "run: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--ledger-out <file>", "run: write the in-memory ledger as JSON").option("--agents <fake|live>", "run: fake agent steps (default) or call the dev API").option("--now <iso>", "run: virtual clock start").option("--park <id>", "run: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "run: collapse retry backoff waits to 0").option("--real-time", "run: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "run: back ctx.artefacts.* on disk").option("--workspace <dir>", "run: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").option("--env <KEY=value>", "run: local env.template() overlay (repeatable; missing key \u21D2 exit 2)", collect, []).option("--max-ticks <n>", "run (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--until <iso>", "archive-runs: window end").option("--out <dir>", "archive-runs: destination dir (archive-index.ndjson + <runId>.zip) \xB7 export: target dir").option("--concurrency <n>", "archive-runs: parallel exports (default 2, max 5)").option("--no-inputs", "archive-runs: exclude run inputs from the bundle").option("--no-artefacts", "archive-runs: exclude artefacts from the bundle").option("--retention-days <n>", "archive-runs: org run retention (default 90) \u2014 --since must fit retention \u2212 7 d").option("--connection <id>", "archive-runs: org storage connection for s3:// / gs:// sinks").option("--release", "workspace: release the run workspace (R42)").option("--attempt <n>", "job-logs: attempt (default latest)").option("--tail <n>", "job-logs: log lines (1..2000, default 200)").option("--objective <text>", "goals create: the objective (\u2264 2000 chars)").option("--judge-predicate <spec>", "goals create: deterministic judge \u2014 '<path> <op> [value]' (e.g. 'output.done truthy'), JSON or @file").option("--judge-agent <agentId|'$self'>", "goals create: judge agent \u2014 write '$self' quoted (or self); it needs --judge-role").option("--judge-role <json|@file>", "goals create: D25 role {name,instructions,tools} for a '$self' judge").option("--schema <json|@file>", "goals create: judge output JsonSchema (must declare a boolean `done`)").option("--cadence <cron|json|@file>", "goals create/schedules create: cadence entry (repeatable, \u2264 5); goals: none \u21D2 immediate", collect, []).option("--timezone <tz>", "goals create/edit/schedules create: IANA timezone for bare cron cadences").option("--notify <emailApp|email|app|off>", "schedules create: where each fire notifies").option("--backfill-on-enable <n|none>", "schedules create/patch/resume: replay \u2264 n missed fires on re-enable (patch: none clears it)").option("--backfill-now", "schedules resume (or patch --paused false): one-shot backfill on THIS re-enable").option("--paused <true|false>", "schedules patch: pause (true) or re-enable (false) the schedule").option("--max-runs <n>", "goals create/edit/raise: iteration cap (1..100)").option("--max-total-credits <n>", "goals create: lineage-wide credit gate").option("--max-credits <n|none>", "goals edit/raise: lineage-wide credit gate (edit: none clears it)").option("--every <interval>", "goals edit/schedules create: interval cadence \u2014 900, 15m, 2h (whole minutes, \u2265 60s)").option("--if-match <updatedAt>", "goals edit/raise: refuse the write if the goal changed since this updatedAt").addHelpText("after", `
69977
+ program2.command("workflows [action] [target] [extra]").description("\u{1F9ED} Manage workflows and their runs (list, start, watch, cancel, resume, retry-step, resolve-step, raise-budget, approve, signal, replay, export, schedules)").option("-i, --workflow-name <name>", "Workflow name (or id)").option("-r, --run-id <id>", "Run id").option("-v, --workflow-version <ver>", "deploy/start/export/goals create/schedules create: a semver, 'latest' (newest push) or a version id").option("--json", "Print the raw {success,data} envelope").option("--all", "list: include dynamic workflows").option("--input <json|@file>", "start/run/goals create/schedules create: run input").option("--idempotency-key <key>", "start: idempotency key").option("--correlation-key <key>", "start/runs: correlation key").option("--tag <tag>", "start/runs/schedules create: tag (repeatable, \u2264 10)", collect, []).option("--budget-credits <n>", "start/schedules create: run budget (credits)").option("--wait <s>", "start: server long-poll \u2264 55 s").option("--follow", "start/logs: attach watch").option("--status <status>", "runs/goals list: filter by status").option("--workflow <name>", "runs: filter by workflow").option("--limit <n>", "runs/goals list/approval-payload --path: page size").option("--cursor <c>", "runs/goals list/approval-payload --path: page cursor").option("--sort <field>", "runs: -createdAt|createdAt|-durationMs|durationMs").option("--steps", "status: per-step table").option("--strict", "status: exit 4/5 by terminal status, 6 for a gated run").option("--compose <on|off>", "policy models set: may the composer be shown the model menu").option("--max-class <fast|balanced|strong>", "policy models set: ceiling for class requests and the class map").option("--consent-actions <n>", "policy models set: estimated actions above which a composed run shows the card").option("--allow <codes>", "policy models set: comma-separated approved codes to narrow the set to").option("--class-fast <codes>", "policy models set: ordered overrides of the fast class (comma-separated)").option("--class-balanced <codes>", "policy models set: ordered overrides of the balanced class (comma-separated)").option("--class-strong <codes>", "policy models set: ordered overrides of the strong class (comma-separated)").option("--class-map <pairs>", "policy models set: comma-separated <taskClass>=<modelClass> pairs").option("--pins <class-only|allowed>", "policy models set: may a composed definition pin a concrete code").option("--fallback <same-class|same-provider>", "policy models set: where a failed leg may hop to").option("--enabled <on|off>", "policy autonomy set: may a start inside the envelope skip the consent card").option("--max-duration <seconds>", "policy autonomy set: the envelope\u2019s duration bound, in seconds").option("--max-actions <n>", "policy autonomy set: the envelope\u2019s estimated-actions bound").option("--max-runs-per-hour <n>", "policy autonomy set: autonomous starts per hour, per agent").option("--forms <list>", "policy autonomy set: comma-separated forms the envelope admits (graph,script,static)").option("--clear <keys>", "policy autonomy set: comma-separated members to clear back to the platform default").option("--agent <agentId>", "policy autonomy: not supported \u2014 no route writes a per-agent envelope (see the refusal)").option("--kind <kind>", "clear-gate: the gate kind to clear (e.g. model_policy)").option("--after <seq>", "watch: replay from seq").option("--timeout <s>", "watch / start --follow: give up after s seconds while the run is still live (exit 7)").option("--events", "watch: print raw frames").option("--wait-for-human", "watch / start --follow: keep following through a human boundary (approval, input, signal, park) instead of exiting 8").option("--reason <text>", "cancel: reason").option("--step <id>", "resume/retry-step/resolve-step/logs: step id").option("--outcome <skip|complete|fail>", "resolve-step: the decision on the parked step (--action is accepted too)").option("--action <skip|complete|fail>", "resolve-step: alias of --outcome").option("--output <json|@file>", "resolve-step: the output a completed step would have produced (required with --outcome complete)").option("--credits <n>", "raise-budget: the new priced cap of a budget-parked run, in the run's own budget unit \u2014 credits, or actions on a seat plan (above the current one)").option("--max-steps <n>", "raise-budget: the new step cap").option("--max-job-seconds <n>", "raise-budget: the new Job-tier seconds cap").option("--max-duration-seconds <n>", "raise-budget: the new wall-clock cap").option("--data <json|@file>", "resume: resumeData").option("--approval <id>", "approve/approval-payload: approval id").option("--path <array.path>", "approval-payload: page one array of a large payload (--cursor / --limit inside it)").option("--decision <approve|deny>", "approve: decision (default approve)").option("--note <text>", "approve/retry-step/resolve-step/raise-budget/goals close: note").option("--edit <json|@file>", "approve: edited payload (small inline edits)").option("--fingerprint <f>", "approve: payloadFingerprint (mandatory with --edit)").option("--payload <json|@file>", "signal: payload").option("--dedupe-key <key>", "signal: dedupe key").option("--local", "replay: replay locally against the compiled artifact").option("--since <dur>", "logs: window").option("--yes", "delete/delete-run/schedules delete: skip confirmation").option("--force", "delete: cancel in-flight runs first \xB7 run: seed from a changed graph \xB7 export: overwrite files").option("--step-output <id=json>", "run: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "run: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "run: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "run: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "run: seed completed steps from a real run").option("--record <dir>", "run: record agent/tool outputs as fixtures").option("--fixtures <dir>", "run: replay recorded fixtures").option("--step-wall <s>", "run: per-step wall in seconds (default 600)").option("--job-wall <s>", "run: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--ledger-out <file>", "run: write the in-memory ledger as JSON").option("--agents <fake|live>", "run: fake agent steps (default) or call the dev API").option("--now <iso>", "run: virtual clock start").option("--park <id>", "run: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "run: collapse retry backoff waits to 0").option("--real-time", "run: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "run: back ctx.artefacts.* on disk").option("--workspace <dir>", "run: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").option("--env <KEY=value>", "run: local env.template() overlay (repeatable; missing key \u21D2 exit 2)", collect, []).option("--max-ticks <n>", "run (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--until <iso>", "archive-runs: window end").option("--out <dir>", "archive-runs: destination dir (archive-index.ndjson + <runId>.zip) \xB7 export: target dir").option("--concurrency <n>", "archive-runs: parallel exports (default 2, max 5)").option("--no-inputs", "archive-runs: exclude run inputs from the bundle").option("--no-artefacts", "archive-runs: exclude artefacts from the bundle").option("--retention-days <n>", "archive-runs: org run retention (default 90) \u2014 --since must fit retention \u2212 7 d").option("--connection <id>", "archive-runs: org storage connection for s3:// / gs:// sinks").option("--release", "workspace: release the run workspace (R42)").option("--attempt <n>", "job-logs: attempt (default latest)").option("--tail <n>", "job-logs: log lines (1..2000, default 200)").option("--objective <text>", "goals create: the objective (\u2264 2000 chars)").option("--judge-predicate <spec>", "goals create: deterministic judge \u2014 '<path> <op> [value]' (e.g. 'output.done truthy'), JSON or @file").option("--judge-agent <agentId|'$self'>", "goals create: judge agent \u2014 write '$self' quoted (or self); it needs --judge-role").option("--judge-role <json|@file>", "goals create: D25 role {name,instructions,tools} for a '$self' judge").option("--schema <json|@file>", "goals create: judge output JsonSchema (must declare a boolean `done`)").option("--cadence <cron|json|@file>", "goals create/schedules create: cadence entry (repeatable, \u2264 5); goals: none \u21D2 immediate", collect, []).option("--timezone <tz>", "goals create/edit/schedules create: IANA timezone for bare cron cadences").option("--notify <emailApp|email|app|off>", "schedules create: where each fire notifies").option("--backfill-on-enable <n|none>", "schedules create/patch/resume: replay \u2264 n missed fires on re-enable (patch: none clears it)").option("--backfill-now", "schedules resume (or patch --paused false): one-shot backfill on THIS re-enable").option("--paused <true|false>", "schedules patch: pause (true) or re-enable (false) the schedule").option("--max-runs <n>", "goals create/edit/raise: iteration cap (1..100)").option("--max-total-credits <n>", "goals create: lineage-wide credit gate").option("--max-credits <n|none>", "goals edit/raise: lineage-wide credit gate (edit: none clears it)").option("--every <interval>", "goals edit/schedules create: interval cadence \u2014 900, 15m, 2h (whole minutes, \u2265 60s)").option("--if-match <updatedAt>", "goals edit/raise: refuse the write if the goal changed since this updatedAt").addHelpText("after", `
67812
69978
  Arguments:
67813
69979
  action list \xB7 view \xB7 versions \xB7 deploy \xB7 activate \xB7 deactivate \xB7 start \xB7 run \xB7 runs \xB7 status \xB7
67814
69980
  watch \xB7 cancel \xB7 resume \xB7 retry-step \xB7 resolve-step \xB7 raise-budget \xB7 approve \xB7 approval-payload \xB7
@@ -68228,7 +70394,7 @@ __name(visibleLength, "visibleLength");
68228
70394
  function printBanner(opts) {
68229
70395
  if (process.env.LUA_NO_BANNER) return;
68230
70396
  if (!process.stdout.isTTY && !process.env.LUA_FORCE_BANNER) return;
68231
- if (process.env.NO_COLOR) {
70397
+ if (noColorEnabled()) {
68232
70398
  process.stdout.write(`
68233
70399
  lua-cli v${opts.version} \xB7 Build and deploy AI agents with superpowers
68234
70400
 
@@ -68251,6 +70417,7 @@ __name(printBanner, "printBanner");
68251
70417
  // src/index.ts
68252
70418
  init_package_root();
68253
70419
  var CLI_VERSION = getCliVersion();
70420
+ configureChalkColor(chalk5);
68254
70421
  var args = process.argv.slice(2);
68255
70422
  var isBareInvocation = args.length === 0;
68256
70423
  var isHelpInvocation = args.includes("--help") || args.includes("-h");