lua-cli 3.41.0 → 3.42.0-beta.1

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
@@ -1752,24 +1752,24 @@ function composePresetEndpoint(type, endpoint) {
1752
1752
  reason: "invalid_url"
1753
1753
  };
1754
1754
  const origin = url.origin;
1755
- const path27 = url.pathname.replace(/\/+$/, "");
1755
+ const path30 = url.pathname.replace(/\/+$/, "");
1756
1756
  if (!rule.segment) {
1757
- const base2 = path27.endsWith(rule.path) ? path27.slice(0, path27.length - rule.path.length) : path27;
1757
+ const base2 = path30.endsWith(rule.path) ? path30.slice(0, path30.length - rule.path.length) : path30;
1758
1758
  return {
1759
1759
  ok: true,
1760
1760
  endpoint: `${origin}${base2}${rule.path}`
1761
1761
  };
1762
1762
  }
1763
1763
  const marker = `${rule.path}/`;
1764
- const at = path27.indexOf(marker);
1764
+ const at = path30.indexOf(marker);
1765
1765
  if (at < 0) {
1766
1766
  return {
1767
1767
  ok: false,
1768
1768
  reason: rule.segment === "dataset" ? "missing_dataset" : "missing_token"
1769
1769
  };
1770
1770
  }
1771
- const base = path27.slice(0, at);
1772
- const afterMarker = path27.slice(at + marker.length);
1771
+ const base = path30.slice(0, at);
1772
+ const afterMarker = path30.slice(at + marker.length);
1773
1773
  const rest = rule.suffix && afterMarker.endsWith(rule.suffix) ? afterMarker.slice(0, afterMarker.length - rule.suffix.length) : afterMarker;
1774
1774
  if (!rest || rest.includes("/")) {
1775
1775
  return {
@@ -2467,7 +2467,7 @@ function scheduledWorkflowRunId(jobId, scheduledTime) {
2467
2467
  }
2468
2468
  function renderWorkflowScheduleKeyTemplate(template3, ctx) {
2469
2469
  if (!template3) return void 0;
2470
- const read = /* @__PURE__ */ __name2((path27) => path27.split(".").reduce((o, k) => o && typeof o === "object" ? o[k] : void 0, ctx.input), "read");
2470
+ const read = /* @__PURE__ */ __name2((path30) => path30.split(".").reduce((o, k) => o && typeof o === "object" ? o[k] : void 0, ctx.input), "read");
2471
2471
  let unresolved = false;
2472
2472
  const out = template3.replace(/\$\{\s*([a-zA-Z0-9_.]+)\s*\}/g, (_m, expr) => {
2473
2473
  let v = "";
@@ -3345,7 +3345,7 @@ function envTemplatePlaceholder(key) {
3345
3345
  __envRef: key
3346
3346
  };
3347
3347
  }
3348
- var __defProp2, __name2, PLUMBING_THREAD_CHANNELS, CHANNEL_CONVERSATION_CHANNELS, HIDDEN_THREAD_CHANNELS, PLATFORM_INVOCATION_CALLERS, SUPERVISION_OPS, 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, LOG_FIELDS_MAX_KEYS, LOG_FIELD_MAX_VALUE_BYTES, LOG_FIELDS_MAX_BYTES, LOG_FIELD_KEY_PATTERN, LOG_FIELD_SECRET_SHAPED_KEY_PATTERNS, LOG_FIELDS_CLAMPED_KEY, LOG_FIELD_ARRAY_COUNT_SUFFIX, LuaLogFieldsError, 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, AGENT_LOG_FIELDS_MAX_WIRE_KEYS, LOG_DRAIN_TYPES_PHASE1, LOG_DRAIN_TYPES_PHASE2, LOG_DRAIN_TYPES_IMPLEMENTED, 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_NEWRELIC_SITES, LOG_DRAIN_ENDPOINT_TEMPLATES, LOG_DRAIN_PRESET_SITES, LOG_DRAIN_BUCKET_NAME_PATTERN, LOG_DRAIN_BUCKET_HOST_PATTERNS, LOG_DRAIN_PRESET_HOST_SUFFIXES, DRAIN_CONTENT_ACK_ERROR_CODE, DRAIN_CONTENT_ACK_FIELD, DrainContentAckRequiredError, LOG_DRAIN_PRESET_PATHS, PRESET_SEGMENT_RE, 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_EXPORT_SOURCES, LOG_EXPORT_MAX_WINDOW_DAYS, LOG_EXPORT_MAX_WINDOW_MS, LOG_EXPORT_URL_TTL_SECONDS, LOG_EXPORT_FILE_RETENTION_DAYS, LOG_EXPORT_FILE_RETENTION_MS, LOG_EXPORT_PART_MAX_BYTES, LOG_DRAIN_NOTICE_RATE_LIMIT_MS, LOG_DRAIN_VERIFICATION_STALE_MS, LOG_DRAIN_DIGEST_INTERVAL_MS, LOG_DRAIN_DIGEST_MAX_ENTRIES, LOG_DRAIN_NOTICE_ERROR_MAX_CHARS, LOG_DRAIN_USAGE_LINE_ITEM, LOG_DRAIN_BYTES_PER_GIB, DEFAULT_LOG_DRAIN_PLAN_CONFIG, TIERS, 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, SCHEDULE_HEALTH_NOTIFY_CHANNELS, SCHEDULE_HEALTH_DEFAULT_NOTIFY, SCHEDULE_HEALTH_LIMITS, KNOWN_KEYS, 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, ONBOARDING_FLOW_KEY, ONBOARDING_SCHEMA_VERSION, id, copy, onboardingHeadingSchema, onboardingRichTextInlineSchema, onboardingRichTextSchema, onboardingDestinationSchema, onboardingArtworkSchema, onboardingAnswerSchema, onboardingQuestionSchema, onboardingTemplateSectionSchema, onboardingTemplateContentSchema, onboardingUnavailableOutcomeSchema, onboardingFlowSchema, onboardingActorSchema, onboardingEditorSchema, onboardingPublicationSummarySchema, onboardingValidationIssueSchema, onboardingValidationResponseSchema, onboardingAssetSchema, onboardingPublicAssetSchema, getOnboardingConfigResponseSchema, saveOnboardingDraftRequestSchema, validateOnboardingRequestSchema, publishOnboardingRequestSchema, rollbackOnboardingRequestSchema, onboardingTemplateCatalogItemSchema, getPublishedOnboardingResponseSchema, WEB_ROUTE_PROTOCOL_VERSION, WEB_ROUTE_METHODS, WEB_ROUTE_REQUEST_HEADER_ALLOWLIST, WEB_ROUTE_RESPONSE_HEADER_ALLOWLIST, WEB_ROUTE_MAX_REQUEST_BODY_BYTES, WEB_ROUTE_MAX_RESPONSE_BYTES, EVIDENCE_SESSION_IDLE_MS, EVIDENCE_MIN_CONVERSATIONS, EVIDENCE_LOW_CONFIDENCE_N, EVIDENCE_SNIPPET_MAX_CHARS, EVIDENCE_EXAMPLES_PER_ITEM, EVIDENCE_SHOWN_ITEMS, EVIDENCE_LATENCY_BOUNDS_MS, EVIDENCE_WORD_BOUNDS, BAD_CONVERSATION_REASONS, ATTACHMENT_LINES, REVIEW_VERDICT_REASON_ORDER, ENV_TEMPLATE_SECRET_KEY_RE;
3348
+ var __defProp2, __name2, PLUMBING_THREAD_CHANNELS, CHANNEL_CONVERSATION_CHANNELS, HIDDEN_THREAD_CHANNELS, PLATFORM_INVOCATION_CALLERS, SUPERVISION_OPS, 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, LOG_FIELDS_MAX_KEYS, LOG_FIELD_MAX_VALUE_BYTES, LOG_FIELDS_MAX_BYTES, LOG_FIELD_KEY_PATTERN, LOG_FIELD_SECRET_SHAPED_KEY_PATTERNS, LOG_FIELDS_CLAMPED_KEY, LOG_FIELD_ARRAY_COUNT_SUFFIX, LuaLogFieldsError, 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, AGENT_LOG_FIELDS_MAX_WIRE_KEYS, LOG_DRAIN_TYPES_PHASE1, LOG_DRAIN_TYPES_PHASE2, LOG_DRAIN_TYPES_IMPLEMENTED, 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_NEWRELIC_SITES, LOG_DRAIN_ENDPOINT_TEMPLATES, LOG_DRAIN_PRESET_SITES, LOG_DRAIN_BUCKET_NAME_PATTERN, LOG_DRAIN_BUCKET_HOST_PATTERNS, LOG_DRAIN_PRESET_HOST_SUFFIXES, DRAIN_CONTENT_ACK_ERROR_CODE, DRAIN_CONTENT_ACK_FIELD, DrainContentAckRequiredError, LOG_DRAIN_PRESET_PATHS, PRESET_SEGMENT_RE, 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_EXPORT_SOURCES, LOG_EXPORT_MAX_WINDOW_DAYS, LOG_EXPORT_MAX_WINDOW_MS, LOG_EXPORT_URL_TTL_SECONDS, LOG_EXPORT_FILE_RETENTION_DAYS, LOG_EXPORT_FILE_RETENTION_MS, LOG_EXPORT_PART_MAX_BYTES, LOG_DRAIN_NOTICE_RATE_LIMIT_MS, LOG_DRAIN_VERIFICATION_STALE_MS, LOG_DRAIN_DIGEST_INTERVAL_MS, LOG_DRAIN_DIGEST_MAX_ENTRIES, LOG_DRAIN_NOTICE_ERROR_MAX_CHARS, LOG_DRAIN_USAGE_LINE_ITEM, LOG_DRAIN_BYTES_PER_GIB, DEFAULT_LOG_DRAIN_PLAN_CONFIG, TIERS, 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, SCHEDULE_HEALTH_NOTIFY_CHANNELS, SCHEDULE_HEALTH_DEFAULT_NOTIFY, SCHEDULE_HEALTH_LIMITS, KNOWN_KEYS, 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, ONBOARDING_FLOW_KEY, ONBOARDING_SCHEMA_VERSION, id, copy, onboardingHeadingSchema, onboardingRichTextInlineSchema, onboardingRichTextSchema, onboardingDestinationSchema, onboardingArtworkSchema, onboardingAnswerSchema, onboardingQuestionSchema, onboardingTemplateSectionSchema, onboardingInstallScreenSchema, onboardingResultScreenSchema, onboardingConnectScreenSchema, onboardingTemplateContentSchema, onboardingUnavailableOutcomeSchema, onboardingFlowSchema, onboardingActorSchema, onboardingEditorSchema, onboardingPublicationSummarySchema, onboardingValidationIssueSchema, onboardingValidationResponseSchema, onboardingAssetSchema, onboardingPublicAssetSchema, getOnboardingConfigResponseSchema, saveOnboardingDraftRequestSchema, validateOnboardingRequestSchema, publishOnboardingRequestSchema, rollbackOnboardingRequestSchema, onboardingTemplateCatalogItemSchema, getPublishedOnboardingResponseSchema, WEB_ROUTE_PROTOCOL_VERSION, WEB_ROUTE_METHODS, WEB_ROUTE_REQUEST_HEADER_ALLOWLIST, WEB_ROUTE_RESPONSE_HEADER_ALLOWLIST, WEB_ROUTE_MAX_WALL_MS, WEB_ROUTE_MAX_REQUEST_BODY_BYTES, WEB_ROUTE_MAX_RESPONSE_BYTES, WEB_APP_NAME_PATTERN, WEB_APP_ENTRY_HTML_MAX_CHARS, WEB_APP_MAX_ASSETS, WEB_APP_ASSET_PATH_PATTERN, WEB_APP_ASSET_PATH_MAX_CHARS, WEB_APP_MAX_ROUTES, WEB_APP_MAX_ASSET_BYTES, WEB_APP_CSP_NONCE_PLACEHOLDER, EVIDENCE_SESSION_IDLE_MS, EVIDENCE_MIN_CONVERSATIONS, EVIDENCE_LOW_CONFIDENCE_N, EVIDENCE_SNIPPET_MAX_CHARS, EVIDENCE_EXAMPLES_PER_ITEM, EVIDENCE_SHOWN_ITEMS, EVIDENCE_LATENCY_BOUNDS_MS, EVIDENCE_WORD_BOUNDS, BAD_CONVERSATION_REASONS, ATTACHMENT_LINES, REVIEW_VERDICT_REASON_ORDER, ENV_TEMPLATE_SECRET_KEY_RE;
3349
3349
  var init_dist = __esm({
3350
3350
  "../shared-types/dist/index.mjs"() {
3351
3351
  "use strict";
@@ -3428,7 +3428,8 @@ var init_dist = __esm({
3428
3428
  "sendWebchatMessage",
3429
3429
  "sendTeamsMessage",
3430
3430
  "sendInstagramMessage",
3431
- "sendMessengerMessage"
3431
+ "sendMessengerMessage",
3432
+ "sendSlackMessage"
3432
3433
  ];
3433
3434
  __name(isAllowedReviewableExecuteTool, "isAllowedReviewableExecuteTool");
3434
3435
  __name2(isAllowedReviewableExecuteTool, "isAllowedReviewableExecuteTool");
@@ -5765,6 +5766,24 @@ listed here; never invent a target.`;
5765
5766
  label: copy(100),
5766
5767
  body: onboardingRichTextSchema
5767
5768
  }).strict();
5769
+ onboardingInstallScreenSchema = z4.object({
5770
+ title: onboardingHeadingSchema,
5771
+ subtitle: copy(240),
5772
+ ctaLabel: copy(80),
5773
+ ctaHelperText: onboardingRichTextSchema
5774
+ }).strict();
5775
+ onboardingResultScreenSchema = z4.object({
5776
+ title: onboardingHeadingSchema,
5777
+ subheader: copy(240),
5778
+ inputPlaceholder: copy(120)
5779
+ }).strict();
5780
+ onboardingConnectScreenSchema = z4.object({
5781
+ title: onboardingHeadingSchema,
5782
+ description: onboardingRichTextSchema,
5783
+ ctaLabel: copy(80),
5784
+ neverLine: copy(120),
5785
+ footnote: copy(240)
5786
+ }).strict();
5768
5787
  onboardingTemplateContentSchema = z4.object({
5769
5788
  id,
5770
5789
  templateId: copy(160),
@@ -5777,7 +5796,10 @@ listed here; never invent a target.`;
5777
5796
  artwork: onboardingArtworkSchema,
5778
5797
  hireButtonLabel: copy(80),
5779
5798
  changeRoleButtonLabel: copy(80),
5780
- nextStepText: onboardingRichTextSchema
5799
+ nextStepText: onboardingRichTextSchema,
5800
+ install: onboardingInstallScreenSchema.optional(),
5801
+ result: onboardingResultScreenSchema.optional(),
5802
+ connect: onboardingConnectScreenSchema.optional()
5781
5803
  }).strict();
5782
5804
  onboardingUnavailableOutcomeSchema = z4.object({
5783
5805
  id,
@@ -5937,6 +5959,7 @@ listed here; never invent a target.`;
5937
5959
  __name2(isForwardableWebRouteRequestHeader, "isForwardableWebRouteRequestHeader");
5938
5960
  __name(isForwardableWebRouteResponseHeader, "isForwardableWebRouteResponseHeader");
5939
5961
  __name2(isForwardableWebRouteResponseHeader, "isForwardableWebRouteResponseHeader");
5962
+ WEB_ROUTE_MAX_WALL_MS = 6e4;
5940
5963
  WEB_ROUTE_MAX_REQUEST_BODY_BYTES = 1 * 1024 * 1024;
5941
5964
  WEB_ROUTE_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
5942
5965
  __name(isWebRouteBody, "isWebRouteBody");
@@ -5947,6 +5970,14 @@ listed here; never invent a target.`;
5947
5970
  __name2(isQueryRecord, "isQueryRecord");
5948
5971
  __name(isWebRouteInput, "isWebRouteInput");
5949
5972
  __name2(isWebRouteInput, "isWebRouteInput");
5973
+ WEB_APP_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
5974
+ WEB_APP_ENTRY_HTML_MAX_CHARS = 256 * 1024;
5975
+ WEB_APP_MAX_ASSETS = 1e3;
5976
+ WEB_APP_ASSET_PATH_PATTERN = /^assets\/(?!.*\.\.)[A-Za-z0-9._\-/]+$/;
5977
+ WEB_APP_ASSET_PATH_MAX_CHARS = 512;
5978
+ WEB_APP_MAX_ROUTES = 200;
5979
+ WEB_APP_MAX_ASSET_BYTES = 8 * 1024 * 1024;
5980
+ WEB_APP_CSP_NONCE_PLACEHOLDER = "__LUA_CSP_NONCE__";
5950
5981
  EVIDENCE_SESSION_IDLE_MS = 30 * 6e4;
5951
5982
  EVIDENCE_MIN_CONVERSATIONS = 20;
5952
5983
  EVIDENCE_LOW_CONFIDENCE_N = 5;
@@ -6125,8 +6156,8 @@ function uidFromFirebaseIdToken(idToken) {
6125
6156
  if (parsed.success) return parsed.data.sub;
6126
6157
  throw new Error(INVALID_FIREBASE_SESSION);
6127
6158
  }
6128
- function parseFirebaseSession(json, now = Date.now()) {
6129
- const customTokenResponse = customTokenResponseSchema.safeParse(json);
6159
+ function parseFirebaseSession(json2, now = Date.now()) {
6160
+ const customTokenResponse = customTokenResponseSchema.safeParse(json2);
6130
6161
  if (customTokenResponse.success) {
6131
6162
  const { idToken: idToken2, refreshToken: refreshToken2, expiresIn: expiresIn2, localId } = customTokenResponse.data;
6132
6163
  return {
@@ -6136,7 +6167,7 @@ function parseFirebaseSession(json, now = Date.now()) {
6136
6167
  uid: localId ?? uidFromFirebaseIdToken(idToken2)
6137
6168
  };
6138
6169
  }
6139
- const refreshResponse = refreshResponseSchema.safeParse(json);
6170
+ const refreshResponse = refreshResponseSchema.safeParse(json2);
6140
6171
  if (!refreshResponse.success) throw new Error(INVALID_FIREBASE_SESSION);
6141
6172
  const { id_token: idToken, refresh_token: refreshToken, expires_in: expiresIn, user_id: uid } = refreshResponse.data;
6142
6173
  return {
@@ -6727,9 +6758,9 @@ function workflowJobTargetProblem(target, plugins, luaApi) {
6727
6758
  if (!hasPlugins && luaApi !== true) return "`target` is legal only beside `plugins` or `luaApi: true`";
6728
6759
  return null;
6729
6760
  }
6730
- function readInitDataPath(root, path27) {
6761
+ function readInitDataPath(root, path30) {
6731
6762
  let cur = root;
6732
- for (const seg of path27.split(".")) {
6763
+ for (const seg of path30.split(".")) {
6733
6764
  const name = seg.replace(/\[(?:0|[1-9]\d*)\]/g, "");
6734
6765
  const indexes = [
6735
6766
  ...seg.matchAll(/\[(0|[1-9]\d*)\]/g)
@@ -6762,25 +6793,25 @@ function renderWorkflowJobTarget(target, initData) {
6762
6793
  templated: false
6763
6794
  };
6764
6795
  }
6765
- const path27 = workflowJobTargetTemplatePath(agentId);
6766
- if (path27 === null) return {
6796
+ const path30 = workflowJobTargetTemplatePath(agentId);
6797
+ if (path30 === null) return {
6767
6798
  ok: false,
6768
6799
  reason: "invalid",
6769
6800
  message: "target.agentId is not a valid initData template"
6770
6801
  };
6771
- const value3 = readInitDataPath(initData, path27);
6802
+ const value3 = readInitDataPath(initData, path30);
6772
6803
  if (value3 === void 0 || value3 === null) {
6773
6804
  return {
6774
6805
  ok: false,
6775
6806
  reason: "unbound",
6776
- message: `target_unresolved: the run input has no initData.${path27}`
6807
+ message: `target_unresolved: the run input has no initData.${path30}`
6777
6808
  };
6778
6809
  }
6779
6810
  if (typeof value3 !== "string" || !value3.trim() || value3.length > 256 || /\s/.test(value3.trim())) {
6780
6811
  return {
6781
6812
  ok: false,
6782
6813
  reason: "empty",
6783
- message: `target_unresolved: initData.${path27} is not a non-empty agent id`
6814
+ message: `target_unresolved: initData.${path30} is not a non-empty agent id`
6784
6815
  };
6785
6816
  }
6786
6817
  return {
@@ -6888,13 +6919,13 @@ function parseTemplatePlaceholder(rawExpr) {
6888
6919
  rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
6889
6920
  };
6890
6921
  }
6891
- function traverseMappingPath(root, path27, errorLabel2) {
6892
- if (path27 === "" || path27 === ".") return root;
6893
- const parts = path27.split(".");
6922
+ function traverseMappingPath(root, path30, errorLabel2) {
6923
+ if (path30 === "" || path30 === ".") return root;
6924
+ const parts = path30.split(".");
6894
6925
  let value22 = root;
6895
6926
  for (const part of parts) {
6896
6927
  if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
6897
- else throw new WorkflowTemplateError(`Invalid path ${path27} in ${errorLabel2}`, path27);
6928
+ else throw new WorkflowTemplateError(`Invalid path ${path30} in ${errorLabel2}`, path30);
6898
6929
  }
6899
6930
  return value22;
6900
6931
  }
@@ -7079,11 +7110,11 @@ function parseModelTemplate(model) {
7079
7110
  message: modelTemplateInvalidMessage(model, why)
7080
7111
  };
7081
7112
  }
7082
- const path27 = m[1];
7083
- if (!path27.split(".").every((seg) => PATH_SEGMENT_RE.test(seg))) {
7113
+ const path30 = m[1];
7114
+ if (!path30.split(".").every((seg) => PATH_SEGMENT_RE.test(seg))) {
7084
7115
  return {
7085
7116
  kind: "invalid",
7086
- message: modelTemplateInvalidMessage(model, `the path \`${path27}\` is not a dotted path of names and canonical [n] indexes`)
7117
+ message: modelTemplateInvalidMessage(model, `the path \`${path30}\` is not a dotted path of names and canonical [n] indexes`)
7087
7118
  };
7088
7119
  }
7089
7120
  if (m[2] !== void 0) {
@@ -7102,7 +7133,7 @@ function parseModelTemplate(model) {
7102
7133
  kind: "template",
7103
7134
  template: {
7104
7135
  placeholder: trimmed2,
7105
- path: path27,
7136
+ path: path30,
7106
7137
  default: dflt
7107
7138
  }
7108
7139
  };
@@ -7111,7 +7142,7 @@ function parseModelTemplate(model) {
7111
7142
  kind: "template",
7112
7143
  template: {
7113
7144
  placeholder: trimmed2,
7114
- path: path27
7145
+ path: path30
7115
7146
  }
7116
7147
  };
7117
7148
  }
@@ -7128,9 +7159,9 @@ function staticModelPin(model) {
7128
7159
  if (parsed.kind === "template") return parsed.template.default;
7129
7160
  return void 0;
7130
7161
  }
7131
- function readPath(root, path27) {
7162
+ function readPath(root, path30) {
7132
7163
  let cur = root;
7133
- for (const seg of path27.split(".")) {
7164
+ for (const seg of path30.split(".")) {
7134
7165
  const name = seg.replace(/\[(?:0|[1-9]\d*)\]/g, "");
7135
7166
  const indexes = [
7136
7167
  ...seg.matchAll(/\[(0|[1-9]\d*)\]/g)
@@ -7165,17 +7196,17 @@ function renderModelTemplate(model, initData) {
7165
7196
  model,
7166
7197
  source: "static"
7167
7198
  };
7168
- const { placeholder, path: path27 } = parsed.template;
7199
+ const { placeholder, path: path30 } = parsed.template;
7169
7200
  if (initData !== void 0 && initData !== null && (typeof initData !== "object" || Array.isArray(initData))) {
7170
7201
  return {
7171
7202
  ok: false,
7172
7203
  placeholder,
7173
- path: path27,
7204
+ path: path30,
7174
7205
  reason: "input-not-object",
7175
7206
  message: modelTemplateInputNotObjectMessage(placeholder, initData)
7176
7207
  };
7177
7208
  }
7178
- const value22 = readPath(initData, path27);
7209
+ const value22 = readPath(initData, path30);
7179
7210
  const reason = value22 === void 0 || value22 === null ? "unbound" : typeof value22 !== "string" ? "not-a-string" : value22.trim() ? null : "empty";
7180
7211
  if (reason === null) return {
7181
7212
  ok: true,
@@ -7190,14 +7221,14 @@ function renderModelTemplate(model, initData) {
7190
7221
  return {
7191
7222
  ok: false,
7192
7223
  placeholder,
7193
- path: path27,
7224
+ path: path30,
7194
7225
  reason,
7195
- message: modelTemplateUnboundMessage(placeholder, path27, reason)
7226
+ message: modelTemplateUnboundMessage(placeholder, path30, reason)
7196
7227
  };
7197
7228
  }
7198
- function modelTemplateUnboundMessage(placeholder, path27, reason) {
7199
- const what = reason === "unbound" ? `the run input has no ${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path27}` : reason === "empty" ? `${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path27} is empty on the run input` : `${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path27} on the run input is not a string`;
7200
- return `model placeholder "${placeholder}" is unbound \u2014 ${what} and the placeholder declares no default; pass one in the run input or write \${${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path27}|<provider/model>}`;
7229
+ function modelTemplateUnboundMessage(placeholder, path30, reason) {
7230
+ const what = reason === "unbound" ? `the run input has no ${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path30}` : reason === "empty" ? `${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path30} is empty on the run input` : `${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path30} on the run input is not a string`;
7231
+ return `model placeholder "${placeholder}" is unbound \u2014 ${what} and the placeholder declares no default; pass one in the run input or write \${${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path30}|<provider/model>}`;
7201
7232
  }
7202
7233
  function modelTemplateInputNotObjectMessage(placeholder, initData) {
7203
7234
  const type = Array.isArray(initData) ? "an array" : `a ${typeof initData}`;
@@ -7237,22 +7268,22 @@ function validateApproverBlock(node, opts = {
7237
7268
  path: "approval"
7238
7269
  }) {
7239
7270
  const issues = [];
7240
- const push = /* @__PURE__ */ __name4((code, path27, message, severity = "error") => issues.push({
7271
+ const push = /* @__PURE__ */ __name4((code, path30, message, severity = "error") => issues.push({
7241
7272
  code,
7242
- path: path27,
7273
+ path: path30,
7243
7274
  severity,
7244
7275
  message
7245
7276
  }), "push");
7246
- const checkSpec = /* @__PURE__ */ __name4((spec, path27) => {
7277
+ const checkSpec = /* @__PURE__ */ __name4((spec, path30) => {
7247
7278
  const r = ApproverSpecSchema.safeParse(spec);
7248
7279
  if (!r.success) {
7249
7280
  const users = spec?.users;
7250
- if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path27, `at most ${APPROVER_SPEC_MAX_USERS} users`);
7281
+ if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path30, `at most ${APPROVER_SPEC_MAX_USERS} users`);
7251
7282
  else {
7252
7283
  const refusal2 = describeApproverSpecRefusal(spec);
7253
7284
  issues.push({
7254
7285
  code: "approver-invalid",
7255
- path: path27,
7286
+ path: path30,
7256
7287
  severity: "error",
7257
7288
  message: refusal2.message,
7258
7289
  repair: {
@@ -7265,12 +7296,12 @@ function validateApproverBlock(node, opts = {
7265
7296
  }
7266
7297
  const s = r.data;
7267
7298
  if (typeof s === "object") {
7268
- if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path27, "governance reviewer routing is not enabled for this deployment");
7269
- if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path27, "IdP-group approvers are not enabled for this deployment");
7299
+ if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path30, "governance reviewer routing is not enabled for this deployment");
7300
+ if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path30, "IdP-group approvers are not enabled for this deployment");
7270
7301
  const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
7271
7302
  if (isTemplateBinding(binding)) {
7272
- if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path27}.template`, "binding root must be initData / stepResults / requestContext / state");
7273
- if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path27}.users`, "a customer-reachable workflow may not bind its approver list");
7303
+ if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path30}.template`, "binding root must be initData / stepResults / requestContext / state");
7304
+ if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path30}.users`, "a customer-reachable workflow may not bind its approver list");
7274
7305
  }
7275
7306
  }
7276
7307
  }, "checkSpec");
@@ -7455,8 +7486,8 @@ function withDefaultsFilled(g) {
7455
7486
  function isConnectionKeyShaped(value22) {
7456
7487
  return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
7457
7488
  }
7458
- function connectionKeyUndeclaredMessage(path27, key) {
7459
- return `${path27} '${key}' is neither a connection id nor a declared connections[].key \u2014 declare it: connections: [{ key: '${key}', integrationType: '<catalog slug, e.g. github>' }] and it resolves on any agent`;
7489
+ function connectionKeyUndeclaredMessage(path30, key) {
7490
+ return `${path30} '${key}' is neither a connection id nor a declared connections[].key \u2014 declare it: connections: [{ key: '${key}', integrationType: '<catalog slug, e.g. github>' }] and it resolves on any agent`;
7460
7491
  }
7461
7492
  function classifyModelProvider(model) {
7462
7493
  const m = (model ?? "").trim().toLowerCase();
@@ -7466,10 +7497,10 @@ function classifyModelProvider(model) {
7466
7497
  if (/^(google\/|gemini)/.test(m)) return "google";
7467
7498
  return null;
7468
7499
  }
7469
- function schemaAtPath(schema, path27) {
7500
+ function schemaAtPath(schema, path30) {
7470
7501
  let cur = schema;
7471
7502
  if (!cur || typeof cur !== "object") return void 0;
7472
- for (const seg of path27.split(".").filter(Boolean)) {
7503
+ for (const seg of path30.split(".").filter(Boolean)) {
7473
7504
  const props = cur.properties;
7474
7505
  const next = props?.[seg];
7475
7506
  if (!next || typeof next !== "object") return void 0;
@@ -7536,21 +7567,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
7536
7567
  static: true
7537
7568
  }) {
7538
7569
  const issues = [];
7539
- const err = /* @__PURE__ */ __name4((code, message, path27, stepId) => {
7570
+ const err = /* @__PURE__ */ __name4((code, message, path30, stepId) => {
7540
7571
  issues.push({
7541
7572
  code,
7542
7573
  message,
7543
7574
  severity: "error",
7544
- path: path27,
7575
+ path: path30,
7545
7576
  stepId
7546
7577
  });
7547
7578
  }, "err");
7548
- const warn = /* @__PURE__ */ __name4((code, message, path27, stepId) => {
7579
+ const warn = /* @__PURE__ */ __name4((code, message, path30, stepId) => {
7549
7580
  issues.push({
7550
7581
  code,
7551
7582
  message,
7552
7583
  severity: "warning",
7553
- path: path27,
7584
+ path: path30,
7554
7585
  stepId
7555
7586
  });
7556
7587
  }, "warn");
@@ -7586,19 +7617,19 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
7586
7617
  err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
7587
7618
  }
7588
7619
  (Array.isArray(g.connections) ? g.connections : []).forEach((c, i) => {
7589
- const path27 = `connections.${i}`;
7620
+ const path30 = `connections.${i}`;
7590
7621
  const key = c?.key;
7591
7622
  const integrationType = c?.integrationType;
7592
7623
  if (typeof key !== "string" || !WORKFLOW_CONNECTION_KEY_RE.test(key)) {
7593
- err("connection-declaration-invalid", `connections[${i}].key must match ${WORKFLOW_CONNECTION_KEY_RE}`, `${path27}.key`);
7624
+ err("connection-declaration-invalid", `connections[${i}].key must match ${WORKFLOW_CONNECTION_KEY_RE}`, `${path30}.key`);
7594
7625
  return;
7595
7626
  }
7596
7627
  if (ownKeys.has(key)) {
7597
- err("connection-declaration-invalid", `connections[${i}].key "${key}" is declared twice`, `${path27}.key`);
7628
+ err("connection-declaration-invalid", `connections[${i}].key "${key}" is declared twice`, `${path30}.key`);
7598
7629
  return;
7599
7630
  }
7600
7631
  if (typeof integrationType !== "string" || !integrationType.trim()) {
7601
- err("connection-declaration-invalid", `connections[${i}] ("${key}") needs an integrationType (the catalog slug, e.g. 'github')`, `${path27}.integrationType`);
7632
+ err("connection-declaration-invalid", `connections[${i}] ("${key}") needs an integrationType (the catalog slug, e.g. 'github')`, `${path30}.integrationType`);
7602
7633
  return;
7603
7634
  }
7604
7635
  ownKeys.add(key);
@@ -7612,112 +7643,112 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
7612
7643
  const seen = /* @__PURE__ */ new Map();
7613
7644
  let nodeCount = 0;
7614
7645
  const upstream = /* @__PURE__ */ new Set();
7615
- const checkId = /* @__PURE__ */ __name4((id2, path27) => {
7646
+ const checkId = /* @__PURE__ */ __name4((id2, path30) => {
7616
7647
  nodeCount += 1;
7617
7648
  if (seen.has(id2)) {
7618
- err("duplicate-step-id", `step id "${id2}" is declared twice (first at ${seen.get(id2)})`, path27, id2);
7649
+ err("duplicate-step-id", `step id "${id2}" is declared twice (first at ${seen.get(id2)})`, path30, id2);
7619
7650
  } else {
7620
- seen.set(id2, path27);
7651
+ seen.set(id2, path30);
7621
7652
  }
7622
7653
  }, "checkId");
7623
- const checkPolicyEnums = /* @__PURE__ */ __name4((node, path27) => {
7654
+ const checkPolicyEnums = /* @__PURE__ */ __name4((node, path30) => {
7624
7655
  const id2 = singleId(node);
7625
7656
  const check = /* @__PURE__ */ __name4((member, allowed) => {
7626
7657
  const value22 = node[member];
7627
7658
  if (value22 === void 0 || typeof value22 === "string" && allowed.includes(value22)) return;
7628
- err("invalid-envelope", `\`${member}\` must be ${allowed.map((a) => `'${a}'`).join(" | ")} (got ${JSON.stringify(value22)})`, `${path27}.${member}`, id2);
7659
+ err("invalid-envelope", `\`${member}\` must be ${allowed.map((a) => `'${a}'`).join(" | ")} (got ${JSON.stringify(value22)})`, `${path30}.${member}`, id2);
7629
7660
  }, "check");
7630
7661
  check("sideEffects", WORKFLOW_SIDE_EFFECTS);
7631
7662
  check("jobResources", WORKFLOW_JOB_RESOURCES);
7632
7663
  }, "checkPolicyEnums");
7633
- const checkRetry = /* @__PURE__ */ __name4((node, path27) => {
7664
+ const checkRetry = /* @__PURE__ */ __name4((node, path30) => {
7634
7665
  const r = node.retry;
7635
7666
  if (!r) return;
7636
7667
  const id2 = singleId(node);
7637
7668
  const unknown = unknownWorkflowRetryMembers(r);
7638
- if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path27}.retry`, id2);
7669
+ if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path30}.retry`, id2);
7639
7670
  if (!isWithinWorkflowRetryAttempts(r.maxAttempts)) {
7640
7671
  const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
7641
- err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path27}.retry.maxAttempts`, id2);
7672
+ err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path30}.retry.maxAttempts`, id2);
7642
7673
  }
7643
7674
  const backoffs = retryBackoffs();
7644
7675
  if (r.backoff !== void 0 && !backoffs.includes(r.backoff)) {
7645
7676
  const list = backoffs.map((b) => `'${b}'`).join(" | ");
7646
- err("backoff-invalid", `retry.backoff must be ${list}`, `${path27}.retry.backoff`, id2);
7677
+ err("backoff-invalid", `retry.backoff must be ${list}`, `${path30}.retry.backoff`, id2);
7647
7678
  }
7648
7679
  if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
7649
- err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path27}.retry.backoffSeconds`, id2);
7680
+ err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path30}.retry.backoffSeconds`, id2);
7650
7681
  }
7651
7682
  if (r.maxBackoffSeconds !== void 0) {
7652
7683
  if (r.backoff !== "exponential") {
7653
- err("backoff-invalid", "retry.maxBackoffSeconds is only meaningful with backoff:'exponential'", `${path27}.retry`, id2);
7684
+ err("backoff-invalid", "retry.maxBackoffSeconds is only meaningful with backoff:'exponential'", `${path30}.retry`, id2);
7654
7685
  } else if (r.maxBackoffSeconds <= 0 || r.backoffSeconds !== void 0 && r.maxBackoffSeconds < r.backoffSeconds) {
7655
- err("backoff-invalid", "retry.maxBackoffSeconds must be > 0 and \u2265 backoffSeconds", `${path27}.retry.maxBackoffSeconds`, id2);
7686
+ err("backoff-invalid", "retry.maxBackoffSeconds must be > 0 and \u2265 backoffSeconds", `${path30}.retry.maxBackoffSeconds`, id2);
7656
7687
  }
7657
7688
  }
7658
7689
  }, "checkRetry");
7659
- const checkTimeout = /* @__PURE__ */ __name4((node, path27) => {
7690
+ const checkTimeout = /* @__PURE__ */ __name4((node, path30) => {
7660
7691
  const t = node.timeoutSeconds;
7661
7692
  if (t === void 0) return;
7662
7693
  const id2 = singleId(node);
7663
7694
  if (!Number.isInteger(t) || t < 1) {
7664
- err("timeout-out-of-range", `timeoutSeconds must be an integer \u2265 1 (got ${t})`, `${path27}.timeoutSeconds`, id2);
7695
+ err("timeout-out-of-range", `timeoutSeconds must be an integer \u2265 1 (got ${t})`, `${path30}.timeoutSeconds`, id2);
7665
7696
  return;
7666
7697
  }
7667
7698
  if (node.tier === "job") {
7668
7699
  const cap = node.type === "agent" ? caps.maxJobTimeoutSeconds : caps.maxJobSegmentSeconds;
7669
7700
  if (t > cap) {
7670
- err("job-timeout-exceeds-cap", `timeoutSeconds ${t} exceeds the Job-tier cap of ${cap} s`, `${path27}.timeoutSeconds`, id2);
7701
+ err("job-timeout-exceeds-cap", `timeoutSeconds ${t} exceeds the Job-tier cap of ${cap} s`, `${path30}.timeoutSeconds`, id2);
7671
7702
  } else if (node.type === "agent" && t > caps.maxJobSegmentSeconds && !node.workspace) {
7672
- err("long-job-requires-workspace", `steps longer than 4 h checkpoint to the run workspace \u2014 add workspace:{mount:'rw'} (and workspace on createWorkflow) or split the step`, `${path27}.timeoutSeconds`, id2);
7703
+ err("long-job-requires-workspace", `steps longer than 4 h checkpoint to the run workspace \u2014 add workspace:{mount:'rw'} (and workspace on createWorkflow) or split the step`, `${path30}.timeoutSeconds`, id2);
7673
7704
  }
7674
7705
  } else if (t > caps.maxWorkerTimeoutSeconds) {
7675
- err("timeout-exceeds-tier", `timeoutSeconds ${t} exceeds the worker tier's ${caps.maxWorkerTimeoutSeconds} s \u2014 steps longer than 10 min run on the Job tier: add tier:'job' (up to ${caps.maxJobSegmentSeconds} s)`, `${path27}.timeoutSeconds`, id2);
7706
+ err("timeout-exceeds-tier", `timeoutSeconds ${t} exceeds the worker tier's ${caps.maxWorkerTimeoutSeconds} s \u2014 steps longer than 10 min run on the Job tier: add tier:'job' (up to ${caps.maxJobSegmentSeconds} s)`, `${path30}.timeoutSeconds`, id2);
7676
7707
  }
7677
7708
  }, "checkTimeout");
7678
- const checkSpecialistRole = /* @__PURE__ */ __name4((node, path27) => {
7709
+ const checkSpecialistRole = /* @__PURE__ */ __name4((node, path30) => {
7679
7710
  const role = node.role;
7680
7711
  const hasRef = typeof role.ref === "string";
7681
7712
  const hasInline = role.name !== void 0 || role.instructions !== void 0 || Array.isArray(role.tools) && role.tools.length > 0;
7682
7713
  if (hasRef && hasInline) {
7683
- err("role-ref-and-inline", "a role is either `{ref}` or inline `{name, instructions, tools}`, never both", `${path27}.role`, node.id);
7714
+ err("role-ref-and-inline", "a role is either `{ref}` or inline `{name, instructions, tools}`, never both", `${path30}.role`, node.id);
7684
7715
  }
7685
7716
  if (node.agentId !== "$self") {
7686
- err("ephemeral-base-not-self", "a role runs on the owner only: use '$self'", `${path27}.agentId`, node.id);
7717
+ err("ephemeral-base-not-self", "a role runs on the owner only: use '$self'", `${path30}.agentId`, node.id);
7687
7718
  }
7688
7719
  if (opts.policy && opts.policy.ephemeralSpecialists !== true) {
7689
- err("ephemeral-specialists-disabled", "ephemeral specialists are off for this org/agent (workflowPolicy.ephemeralSpecialists)", `${path27}.role`, node.id);
7720
+ err("ephemeral-specialists-disabled", "ephemeral specialists are off for this org/agent (workflowPolicy.ephemeralSpecialists)", `${path30}.role`, node.id);
7690
7721
  }
7691
7722
  let tools = Array.isArray(role.tools) ? role.tools.filter((t) => typeof t === "string") : [];
7692
7723
  if (hasRef && opts.roleLibrary) {
7693
7724
  const saved = opts.roleLibrary.get(role.ref);
7694
7725
  if (!saved) {
7695
- err("role-ref-unknown", `no saved role \`${role.ref}\` in the owner's library`, `${path27}.role.ref`, node.id);
7726
+ err("role-ref-unknown", `no saved role \`${role.ref}\` in the owner's library`, `${path30}.role.ref`, node.id);
7696
7727
  } else {
7697
7728
  tools = saved.tools ?? [];
7698
7729
  if (typeof role.roleHash === "string" && saved.roleHash && role.roleHash !== saved.roleHash) {
7699
- warn("role-ref-stale", `saved role \`${role.ref}\` changed since this version was frozen`, `${path27}.role.ref`, node.id);
7730
+ warn("role-ref-stale", `saved role \`${role.ref}\` changed since this version was frozen`, `${path30}.role.ref`, node.id);
7700
7731
  }
7701
7732
  }
7702
7733
  }
7703
7734
  if (tools.length && opts.toolCatalog) {
7704
7735
  const missing = tools.filter((t) => !opts.toolCatalog.has(t));
7705
7736
  if (missing.length) {
7706
- err("ephemeral-tools-not-subset", `role tools not in the owner's catalog: ${missing.join(", ")}`, `${path27}.role.tools`, node.id);
7737
+ err("ephemeral-tools-not-subset", `role tools not in the owner's catalog: ${missing.join(", ")}`, `${path30}.role.tools`, node.id);
7707
7738
  }
7708
7739
  }
7709
7740
  }, "checkSpecialistRole");
7710
- const checkRequiredConnections = /* @__PURE__ */ __name4((node, path27) => {
7741
+ const checkRequiredConnections = /* @__PURE__ */ __name4((node, path30) => {
7711
7742
  const required = node.requiredConnections;
7712
7743
  if (!Array.isArray(required)) return;
7713
7744
  const undeclared = required.filter(undeclaredKey);
7714
7745
  if (undeclared.length) {
7715
- err("connection-key-undeclared", connectionKeyUndeclaredMessage(`${path27}.requiredConnections`, undeclared[0]) + (undeclared.length > 1 ? ` (also undeclared: ${JSON.stringify(undeclared.slice(1))})` : ""), `${path27}.requiredConnections`, singleId(node));
7746
+ err("connection-key-undeclared", connectionKeyUndeclaredMessage(`${path30}.requiredConnections`, undeclared[0]) + (undeclared.length > 1 ? ` (also undeclared: ${JSON.stringify(undeclared.slice(1))})` : ""), `${path30}.requiredConnections`, singleId(node));
7716
7747
  }
7717
7748
  if (!opts.connectionIds) return;
7718
7749
  const unknown = required.filter((c) => typeof c !== "string" || !declaredKeys.has(c) && !opts.connectionIds.has(c));
7719
7750
  if (unknown.length) {
7720
- err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are neither declared connections[].key values nor connections the owner can mount`, `${path27}.requiredConnections`, singleId(node));
7751
+ err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are neither declared connections[].key values nor connections the owner can mount`, `${path30}.requiredConnections`, singleId(node));
7721
7752
  }
7722
7753
  }, "checkRequiredConnections");
7723
7754
  const jobClassifiablePin = /* @__PURE__ */ __name4((model) => {
@@ -7735,33 +7766,33 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
7735
7766
  }
7736
7767
  return void 0;
7737
7768
  }, "jobClassifiablePin");
7738
- const checkTier = /* @__PURE__ */ __name4((node, path27) => {
7769
+ const checkTier = /* @__PURE__ */ __name4((node, path30) => {
7739
7770
  const id2 = singleId(node);
7740
7771
  if (node.workspace && node.workspace !== "inherit" && node.tier !== void 0 && node.tier !== "job") {
7741
- err("workspace-requires-job-tier", "a step mounting a workspace must be tier:'job'", `${path27}.workspace`, id2);
7772
+ err("workspace-requires-job-tier", "a step mounting a workspace must be tier:'job'", `${path30}.workspace`, id2);
7742
7773
  }
7743
7774
  if (node.harness !== void 0 && node.tier !== "job") {
7744
- err("harness-requires-job-tier", "`harness` is only legal on a tier:'job' agent step", `${path27}.harness`, id2);
7775
+ err("harness-requires-job-tier", "`harness` is only legal on a tier:'job' agent step", `${path30}.harness`, id2);
7745
7776
  }
7746
7777
  const provider = node.type === "agent" ? classifyModelProvider(jobClassifiablePin(node.model)) : null;
7747
7778
  if (node.harness === "claude-code" && provider !== null && provider !== "anthropic") {
7748
- err("harness-provider-mismatch", `harness:'claude-code' needs an Anthropic model (model "${node.type === "agent" ? node.model : ""}" is ${provider})`, `${path27}.harness`, id2);
7779
+ err("harness-provider-mismatch", `harness:'claude-code' needs an Anthropic model (model "${node.type === "agent" ? node.model : ""}" is ${provider})`, `${path30}.harness`, id2);
7749
7780
  }
7750
7781
  if (isJobTier(node) && provider !== null && opts.policy?.jobProviders && !opts.policy.jobProviders.includes(provider)) {
7751
- err("job-tier-provider-unsupported", `model provider '${provider}' is outside LUA_WF_JOB_PROVIDERS [${opts.policy.jobProviders.join(", ")}]`, `${path27}.model`, id2);
7782
+ err("job-tier-provider-unsupported", `model provider '${provider}' is outside LUA_WF_JOB_PROVIDERS [${opts.policy.jobProviders.join(", ")}]`, `${path30}.model`, id2);
7752
7783
  }
7753
7784
  }, "checkTier");
7754
- const checkModel = /* @__PURE__ */ __name4((node, path27) => {
7785
+ const checkModel = /* @__PURE__ */ __name4((node, path30) => {
7755
7786
  if (node.type !== "agent" || typeof node.model !== "string") return;
7756
7787
  const id2 = singleId(node);
7757
7788
  const parsed = parseModelTemplate(node.model);
7758
7789
  if (parsed.kind === "invalid") {
7759
- err("model-template-invalid", parsed.message, `${path27}.model`, id2);
7790
+ err("model-template-invalid", parsed.message, `${path30}.model`, id2);
7760
7791
  return;
7761
7792
  }
7762
7793
  if (parsed.kind === "template" && parsed.template.default === void 0) {
7763
7794
  if (isJobTier(node)) {
7764
- err("model-template-default-required", modelTemplateDefaultRequiredMessage(node.model), `${path27}.model`, id2);
7795
+ err("model-template-default-required", modelTemplateDefaultRequiredMessage(node.model), `${path30}.model`, id2);
7765
7796
  }
7766
7797
  return;
7767
7798
  }
@@ -7774,40 +7805,40 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
7774
7805
  if (registry === "unavailable") {
7775
7806
  const trimmed2 = pin.trim();
7776
7807
  if (trimmed2 && !normalizeModelId(trimmed2, []).ok) {
7777
- 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)`), `${path27}.model`, id2);
7808
+ 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)`), `${path30}.model`, id2);
7778
7809
  }
7779
7810
  return;
7780
7811
  }
7781
7812
  const resolved = normalizeModelId(pin, registry);
7782
- if (!resolved.ok) err("model-unresolved", inContext(modelUnresolvedMessage(resolved)), `${path27}.model`, id2);
7813
+ if (!resolved.ok) err("model-unresolved", inContext(modelUnresolvedMessage(resolved)), `${path30}.model`, id2);
7783
7814
  }, "checkModel");
7784
- const checkTaskClass = /* @__PURE__ */ __name4((node, path27) => {
7815
+ const checkTaskClass = /* @__PURE__ */ __name4((node, path30) => {
7785
7816
  if (node.type !== "agent" || node.taskClass === void 0) return;
7786
7817
  if (!opts.static) return;
7787
7818
  if (typeof node.model === "string" && node.model.trim()) return;
7788
- err("task-class-without-model", `taskClass '${String(node.taskClass)}' needs a \`model\` on a pushed definition \u2014 write a class (model: 'class/fast' | 'class/balanced' | 'class/strong') or an approved provider/model code; nothing maps a task class to a model at dispatch, so a step with neither would run on the agent\u2019s own model`, `${path27}.taskClass`, singleId(node));
7819
+ err("task-class-without-model", `taskClass '${String(node.taskClass)}' needs a \`model\` on a pushed definition \u2014 write a class (model: 'class/fast' | 'class/balanced' | 'class/strong') or an approved provider/model code; nothing maps a task class to a model at dispatch, so a step with neither would run on the agent\u2019s own model`, `${path30}.taskClass`, singleId(node));
7789
7820
  }, "checkTaskClass");
7790
- const checkWorkspace = /* @__PURE__ */ __name4((node, path27) => {
7821
+ const checkWorkspace = /* @__PURE__ */ __name4((node, path30) => {
7791
7822
  const id2 = singleId(node);
7792
7823
  const ws = workspaceOf(node);
7793
7824
  if (isJobTier(node) && opts.policy && opts.policy.jobTier !== true) {
7794
- err("job-tier-disabled", `"${id2}" runs on the Job tier but the org's workflowPolicy.jobTier is off \u2014 an org admin enables it (lua features workflows.caps.jobTier true)`, `${path27}.tier`, id2);
7825
+ err("job-tier-disabled", `"${id2}" runs on the Job tier but the org's workflowPolicy.jobTier is off \u2014 an org admin enables it (lua features workflows.caps.jobTier true)`, `${path30}.tier`, id2);
7795
7826
  }
7796
7827
  const tools = jobToolsOf(node);
7797
7828
  if (tools !== void 0) {
7798
7829
  if (!isJobTier(node)) {
7799
- err("job-tools-require-job-tier", "`jobTools` needs tier:'job'", `${path27}.jobTools`, id2);
7830
+ err("job-tools-require-job-tier", "`jobTools` needs tier:'job'", `${path30}.jobTools`, id2);
7800
7831
  }
7801
7832
  const unknown = (Array.isArray(tools) ? tools : [
7802
7833
  tools
7803
7834
  ]).filter((t) => !WORKFLOW_JOB_TOOLS.includes(t));
7804
7835
  if (unknown.length > 0) {
7805
- err("job-tools-unknown", `jobTools ${JSON.stringify(unknown)} outside WORKFLOW_JOB_TOOLS [${WORKFLOW_JOB_TOOLS.join(", ")}]`, `${path27}.jobTools`, id2);
7836
+ err("job-tools-unknown", `jobTools ${JSON.stringify(unknown)} outside WORKFLOW_JOB_TOOLS [${WORKFLOW_JOB_TOOLS.join(", ")}]`, `${path30}.jobTools`, id2);
7806
7837
  }
7807
7838
  if (node.type === "agent" && (Array.isArray(tools) ? tools : [
7808
7839
  tools
7809
7840
  ]).includes("gh")) {
7810
- warn("gh-on-coding-turn", `"${id2}" grants the gh tool on a coding turn \u2014 the pod can open/merge PRs on the workspace repo (purpose:'gh' token, pull_requests:write + contents:write)`, `${path27}.jobTools`, id2);
7841
+ warn("gh-on-coding-turn", `"${id2}" grants the gh tool on a coding turn \u2014 the pod can open/merge PRs on the workspace repo (purpose:'gh' token, pull_requests:write + contents:write)`, `${path30}.jobTools`, id2);
7811
7842
  }
7812
7843
  }
7813
7844
  const plugins = node.plugins;
@@ -7815,44 +7846,44 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
7815
7846
  const luaApi = node.luaApi;
7816
7847
  if (luaApi !== void 0) {
7817
7848
  const problem = workflowJobLuaApiProblem(luaApi, node.type === "step" ? "code" : node.type, isJobTier(node) ? "job" : node.tier);
7818
- if (problem) err("lua-api-invalid", problem, `${path27}.luaApi`, id2);
7849
+ if (problem) err("lua-api-invalid", problem, `${path30}.luaApi`, id2);
7819
7850
  }
7820
7851
  if (target !== void 0) {
7821
7852
  const problem = workflowJobTargetProblem(target, plugins, luaApi);
7822
- if (problem) err("target-invalid", problem, `${path27}.target`, id2);
7853
+ if (problem) err("target-invalid", problem, `${path30}.target`, id2);
7823
7854
  }
7824
7855
  if (plugins !== void 0) {
7825
7856
  const problem = workflowJobPluginsProblem(plugins);
7826
- if (problem) err("plugins-invalid", problem, `${path27}.plugins`, id2);
7827
- if (node.type !== "agent") err("plugins-invalid", "`plugins` is legal on agent steps only", `${path27}.plugins`, id2);
7857
+ if (problem) err("plugins-invalid", problem, `${path30}.plugins`, id2);
7858
+ if (node.type !== "agent") err("plugins-invalid", "`plugins` is legal on agent steps only", `${path30}.plugins`, id2);
7828
7859
  else {
7829
- if (!isJobTier(node)) err("plugins-invalid", "`plugins` needs tier:'job'", `${path27}.plugins`, id2);
7860
+ if (!isJobTier(node)) err("plugins-invalid", "`plugins` needs tier:'job'", `${path30}.plugins`, id2);
7830
7861
  const provider = classifyModelProvider(jobClassifiablePin(node.model));
7831
- if (node.harness === "generic" || provider !== null && provider !== "anthropic") err("plugins-invalid", "`plugins` run on the claude-code harness only (an Anthropic model, no `harness:'generic'`)", `${path27}.plugins`, id2);
7862
+ if (node.harness === "generic" || provider !== null && provider !== "anthropic") err("plugins-invalid", "`plugins` run on the claude-code harness only (an Anthropic model, no `harness:'generic'`)", `${path30}.plugins`, id2);
7832
7863
  const w = workspaceOf(node);
7833
7864
  const ro = w !== void 0 && w !== "inherit" && w.mount === "ro";
7834
7865
  const declared = Array.isArray(tools) ? tools : void 0;
7835
- if (!effectiveJobTools(declared, ro).includes("shell")) err("plugins-invalid", "`plugins` need the `shell` job tool (the plugin drives lua-cli) \u2014 keep it in jobTools and mount rw", `${path27}.plugins`, id2);
7866
+ if (!effectiveJobTools(declared, ro).includes("shell")) err("plugins-invalid", "`plugins` need the `shell` job tool (the plugin drives lua-cli) \u2014 keep it in jobTools and mount rw", `${path30}.plugins`, id2);
7836
7867
  }
7837
7868
  }
7838
7869
  if (node.type === "agent" && ws && ws !== "inherit" && ws.mount === "ro" && Array.isArray(tools) && effectiveJobTools(tools, true).length === 0) {
7839
- warn("ro-step-has-no-tools", `"${id2}" mounts the workspace read-only and every jobTool it declares (${JSON.stringify(tools)}) is one the ro mount drops [${WORKFLOW_JOB_READ_ONLY_DROPPED.join(", ")}] \u2014 the coding turn would run with no tools at all; keep a read-only tool (read/glob/grep, gh) or mount rw`, `${path27}.jobTools`, id2);
7870
+ warn("ro-step-has-no-tools", `"${id2}" mounts the workspace read-only and every jobTool it declares (${JSON.stringify(tools)}) is one the ro mount drops [${WORKFLOW_JOB_READ_ONLY_DROPPED.join(", ")}] \u2014 the coding turn would run with no tools at all; keep a read-only tool (read/glob/grep, gh) or mount rw`, `${path30}.jobTools`, id2);
7840
7871
  }
7841
7872
  if (ws && ws !== "inherit") {
7842
7873
  if (!envelopeWorkspace && !opts.mayInherit) {
7843
- err("workspace-not-declared", `"${id2}" mounts a workspace but the workflow declares none \u2014 add workspace:{kind, \u2026} on createWorkflow`, `${path27}.workspace`, id2);
7874
+ err("workspace-not-declared", `"${id2}" mounts a workspace but the workflow declares none \u2014 add workspace:{kind, \u2026} on createWorkflow`, `${path30}.workspace`, id2);
7844
7875
  }
7845
7876
  if (ws.isolation === "worktree" && envelopeWorkspace?.kind === "empty") {
7846
- err("worktree-requires-git-workspace", `"${id2}": isolation:'worktree' needs a kind:'git' workspace (the envelope declares kind:'empty')`, `${path27}.workspace.isolation`, id2);
7877
+ err("worktree-requires-git-workspace", `"${id2}": isolation:'worktree' needs a kind:'git' workspace (the envelope declares kind:'empty')`, `${path30}.workspace.isolation`, id2);
7847
7878
  }
7848
7879
  }
7849
7880
  if (node.type === "workflow" && ws === "inherit") {
7850
7881
  if (!envelopeWorkspace) {
7851
- err("workspace-inherit-without-parent-workspace", `"${id2}": workspace:'inherit' but the parent workflow declares no workspace \u2014 there is nothing to inherit`, `${path27}.workspace`, id2);
7882
+ err("workspace-inherit-without-parent-workspace", `"${id2}": workspace:'inherit' but the parent workflow declares no workspace \u2014 there is nothing to inherit`, `${path30}.workspace`, id2);
7852
7883
  }
7853
7884
  const child = opts.resolveWorkflow?.(node.workflowId);
7854
7885
  if (child?.workspace) {
7855
- err("workspace-inherit-conflict", `"${id2}": workspace:'inherit' on "${node.workflowId}", which declares its own workspace`, `${path27}.workspace`, id2);
7886
+ err("workspace-inherit-conflict", `"${id2}": workspace:'inherit' on "${node.workflowId}", which declares its own workspace`, `${path30}.workspace`, id2);
7856
7887
  }
7857
7888
  }
7858
7889
  }, "checkWorkspace");
@@ -7866,165 +7897,165 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
7866
7897
  warn(MAP_MEMBER_MALFORMED_CODE, mapMemberMalformedMessage(id2, m), `${basePath}.${m.member}`, id2);
7867
7898
  }
7868
7899
  }, "checkMapMembers");
7869
- const checkInputShape = /* @__PURE__ */ __name4((node, path27) => {
7900
+ const checkInputShape = /* @__PURE__ */ __name4((node, path30) => {
7870
7901
  const input = node.input;
7871
7902
  if (input === void 0) return;
7872
7903
  const id2 = singleId(node);
7873
7904
  if (input !== null && typeof input === "object" && !Array.isArray(input)) {
7874
- checkMapMembers(input, `${path27}.input`, id2);
7905
+ checkMapMembers(input, `${path30}.input`, id2);
7875
7906
  return;
7876
7907
  }
7877
- err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path27}.input`, id2);
7908
+ err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path30}.input`, id2);
7878
7909
  }, "checkInputShape");
7879
- const checkBodyInput = /* @__PURE__ */ __name4((body, path27, container) => {
7910
+ const checkBodyInput = /* @__PURE__ */ __name4((body, path30, container) => {
7880
7911
  if (body.type === "workflow" || body.input === void 0) return;
7881
- err("arm-input-unsupported", container === "foreach" ? `a foreach body receives each item as its input \u2014 drop \`input\` on "${singleId(body)}" and map the items before the foreach instead` : `a loop body receives the previous output as its input \u2014 drop \`input\` on "${singleId(body)}" and put the map before the loop instead`, `${path27}.input`, singleId(body));
7912
+ err("arm-input-unsupported", container === "foreach" ? `a foreach body receives each item as its input \u2014 drop \`input\` on "${singleId(body)}" and map the items before the foreach instead` : `a loop body receives the previous output as its input \u2014 drop \`input\` on "${singleId(body)}" and put the map before the loop instead`, `${path30}.input`, singleId(body));
7882
7913
  }, "checkBodyInput");
7883
- const checkSingle = /* @__PURE__ */ __name4((node, path27, depth) => {
7914
+ const checkSingle = /* @__PURE__ */ __name4((node, path30, depth) => {
7884
7915
  recordOutputSchema(node);
7885
7916
  if (node.type === "workflow" && (typeof node.workflowId !== "string" || node.workflowId.length === 0)) {
7886
- checkId(node.id, path27);
7887
- err("invalid-envelope", `\`workflowId\` must be a non-empty string naming the workflow to start (got ${JSON.stringify(node.workflowId)})`, `${path27}.workflowId`, node.id);
7917
+ checkId(node.id, path30);
7918
+ err("invalid-envelope", `\`workflowId\` must be a non-empty string naming the workflow to start (got ${JSON.stringify(node.workflowId)})`, `${path30}.workflowId`, node.id);
7888
7919
  return;
7889
7920
  }
7890
7921
  if (node.type === "workflow" && (node.workflowId.startsWith("$") || Array.isArray(node.graph))) {
7891
- checkId(node.id, path27);
7892
- err("node-type-unsupported-by-engine", armSubrunUnsupportedMessage(node.id, node.workflowId), path27, node.id);
7922
+ checkId(node.id, path30);
7923
+ err("node-type-unsupported-by-engine", armSubrunUnsupportedMessage(node.id, node.workflowId), path30, node.id);
7893
7924
  return;
7894
7925
  }
7895
- checkId(singleId(node), path27);
7896
- checkPolicyEnums(node, path27);
7897
- checkInputShape(node, path27);
7898
- checkTimeout(node, path27);
7899
- checkTier(node, path27);
7900
- checkModel(node, path27);
7901
- checkTaskClass(node, path27);
7902
- checkRetry(node, path27);
7903
- checkWorkspace(node, path27);
7926
+ checkId(singleId(node), path30);
7927
+ checkPolicyEnums(node, path30);
7928
+ checkInputShape(node, path30);
7929
+ checkTimeout(node, path30);
7930
+ checkTier(node, path30);
7931
+ checkModel(node, path30);
7932
+ checkTaskClass(node, path30);
7933
+ checkRetry(node, path30);
7934
+ checkWorkspace(node, path30);
7904
7935
  if (!opts.static) {
7905
- if (node.type === "step") err("code-step-not-allowed", "code steps are static-only (03 \xA73.4.2)", path27, node.step.id);
7936
+ if (node.type === "step") err("code-step-not-allowed", "code steps are static-only (03 \xA73.4.2)", path30, node.step.id);
7906
7937
  if (node.type === "agent" && node.systemPrompt !== void 0) {
7907
- err("persona-override-not-allowed", "`systemPrompt` is static-only (03 \xA73.4.2)", `${path27}.systemPrompt`, node.id);
7938
+ err("persona-override-not-allowed", "`systemPrompt` is static-only (03 \xA73.4.2)", `${path30}.systemPrompt`, node.id);
7908
7939
  }
7909
7940
  }
7910
7941
  if (node.type === "agent" && node.role && node.systemPrompt !== void 0) {
7911
- err("ephemeral-persona-not-overridable", "a specialist role and systemPrompt cannot coexist", `${path27}.systemPrompt`, node.id);
7942
+ err("ephemeral-persona-not-overridable", "a specialist role and systemPrompt cannot coexist", `${path30}.systemPrompt`, node.id);
7912
7943
  }
7913
7944
  if (node.type === "agent" && node.role && typeof node.role.instructions === "string" && node.role.instructions.length > 4e3) {
7914
- err("ephemeral-role-too-long", "role.instructions exceeds 4000 chars", `${path27}.role.instructions`, node.id);
7945
+ err("ephemeral-role-too-long", "role.instructions exceeds 4000 chars", `${path30}.role.instructions`, node.id);
7915
7946
  }
7916
7947
  if (node.type === "agent" && node.role && (node.role.tools?.length ?? 0) > 64) {
7917
- err("cap-exceeded", "role.tools exceeds 64 entries", `${path27}.role.tools`, node.id);
7948
+ err("cap-exceeded", "role.tools exceeds 64 entries", `${path30}.role.tools`, node.id);
7918
7949
  }
7919
- if (node.type === "agent" && node.role) checkSpecialistRole(node, path27);
7920
- checkRequiredConnections(node, path27);
7950
+ if (node.type === "agent" && node.role) checkSpecialistRole(node, path30);
7951
+ checkRequiredConnections(node, path30);
7921
7952
  if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
7922
- err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path27, node.id);
7953
+ err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path30, node.id);
7923
7954
  }
7924
7955
  if (node.type === "workflow" && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
7925
- err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path27, node.id);
7956
+ err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path30, node.id);
7926
7957
  }
7927
7958
  for (const ref of nodeStepRefs(node)) {
7928
7959
  if (!upstream.has(ref)) {
7929
- err("template-reference-unresolved", `"${singleId(node)}" references stepResults.${ref}, which is not upstream`, path27, singleId(node));
7960
+ err("template-reference-unresolved", `"${singleId(node)}" references stepResults.${ref}, which is not upstream`, path30, singleId(node));
7930
7961
  }
7931
7962
  }
7932
7963
  }, "checkSingle");
7933
- const checkHitl = /* @__PURE__ */ __name4((node, path27) => {
7964
+ const checkHitl = /* @__PURE__ */ __name4((node, path30) => {
7934
7965
  if (node.type === "waitForSignal") {
7935
7966
  const w = node;
7936
- checkId(w.id, path27);
7937
- if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path27}.signal`, w.id);
7967
+ checkId(w.id, path30);
7968
+ if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path30}.signal`, w.id);
7938
7969
  return;
7939
7970
  }
7940
7971
  const a = node;
7941
- checkId(a.id, path27);
7972
+ checkId(a.id, path30);
7942
7973
  if ((a.approver ?? "creator") === "creator" && a.excludeInitiator === true) {
7943
- err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path27, a.id);
7974
+ err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path30, a.id);
7944
7975
  }
7945
7976
  const editable = approvalEditable(a);
7946
7977
  if (a.fourEyes !== void 0 && !editable) {
7947
- err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path27}.fourEyes`, a.id);
7978
+ err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path30}.fourEyes`, a.id);
7948
7979
  }
7949
7980
  if (a.editable === false && Array.isArray(a.editablePaths) && a.editablePaths.length > 0) {
7950
- err("editable-path-invalid", "`editablePaths` beside editable:false is contradictory \u2014 drop the paths or set editable:true", `${path27}.editablePaths`, a.id);
7981
+ err("editable-path-invalid", "`editablePaths` beside editable:false is contradictory \u2014 drop the paths or set editable:true", `${path30}.editablePaths`, a.id);
7951
7982
  } else if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && !editable) {
7952
- err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true (a non-empty editablePaths implies it)", `${path27}.editablePaths`, a.id);
7983
+ err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true (a non-empty editablePaths implies it)", `${path30}.editablePaths`, a.id);
7953
7984
  }
7954
7985
  for (const p of a.editablePaths ?? []) {
7955
- if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path27}.editablePaths`, a.id);
7986
+ if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path30}.editablePaths`, a.id);
7956
7987
  }
7957
7988
  if (Array.isArray(a.onTimeout)) {
7958
7989
  const chain = a.onTimeout;
7959
7990
  const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
7960
- if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path27}.onTimeout`, a.id);
7991
+ if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path30}.onTimeout`, a.id);
7961
7992
  const last = chain[chain.length - 1];
7962
7993
  if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
7963
- err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path27}.onTimeout`, a.id);
7994
+ err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path30}.onTimeout`, a.id);
7964
7995
  }
7965
7996
  }
7966
7997
  if (typeof a.details === "string") {
7967
7998
  for (const ref of templateStepRefs(a.details)) {
7968
- if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path27, a.id);
7999
+ if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path30, a.id);
7969
8000
  }
7970
8001
  }
7971
8002
  }, "checkHitl");
7972
- const checkHitlArm = /* @__PURE__ */ __name4((node, path27, container) => {
8003
+ const checkHitlArm = /* @__PURE__ */ __name4((node, path30, container) => {
7973
8004
  if (!workflowContainerRunsHitlArm(container)) {
7974
- checkId(node.id, path27);
7975
- err("node-type-unsupported-in-container", workflowHitlArmUnsupportedMessage(node.type, node.id, container), path27, node.id);
8005
+ checkId(node.id, path30);
8006
+ err("node-type-unsupported-in-container", workflowHitlArmUnsupportedMessage(node.type, node.id, container), path30, node.id);
7976
8007
  return;
7977
8008
  }
7978
- checkHitl(node, path27);
8009
+ checkHitl(node, path30);
7979
8010
  }, "checkHitlArm");
7980
- const checkArm = /* @__PURE__ */ __name4((arm, path27, depth, container) => {
8011
+ const checkArm = /* @__PURE__ */ __name4((arm, path30, depth, container) => {
7981
8012
  if (arm.type === "mapping") {
7982
- checkId(arm.id, path27);
7983
- checkMapMembers(readMapConfig(arm.mapConfig), `${path27}.mapConfig`, arm.id);
8013
+ checkId(arm.id, path30);
8014
+ checkMapMembers(readMapConfig(arm.mapConfig), `${path30}.mapConfig`, arm.id);
7984
8015
  for (const ref of nodeStepRefs(arm)) {
7985
- if (!upstream.has(ref)) err("template-reference-unresolved", `"${arm.id}" references stepResults.${ref}, which is not upstream`, path27, arm.id);
8016
+ if (!upstream.has(ref)) err("template-reference-unresolved", `"${arm.id}" references stepResults.${ref}, which is not upstream`, path30, arm.id);
7986
8017
  }
7987
8018
  return;
7988
8019
  }
7989
8020
  if (isHitlNode(arm)) {
7990
- checkHitlArm(arm, path27, container);
8021
+ checkHitlArm(arm, path30, container);
7991
8022
  return;
7992
8023
  }
7993
- checkSingle(arm, path27, depth);
8024
+ checkSingle(arm, path30, depth);
7994
8025
  }, "checkArm");
7995
8026
  graph.forEach((entry, i) => {
7996
- const path27 = `graph.${i}`;
8027
+ const path30 = `graph.${i}`;
7997
8028
  const declared = [];
7998
8029
  switch (entry.type) {
7999
8030
  case "step":
8000
8031
  case "agent":
8001
8032
  case "tool":
8002
8033
  case "workflow":
8003
- checkSingle(entry, path27, 1);
8034
+ checkSingle(entry, path30, 1);
8004
8035
  declared.push(singleId(entry));
8005
8036
  break;
8006
8037
  case "mapping":
8007
- checkId(entry.id, path27);
8008
- checkMapMembers(readMapConfig(entry.mapConfig), `${path27}.mapConfig`, entry.id);
8038
+ checkId(entry.id, path30);
8039
+ checkMapMembers(readMapConfig(entry.mapConfig), `${path30}.mapConfig`, entry.id);
8009
8040
  for (const ref of nodeStepRefs(entry)) {
8010
- if (!upstream.has(ref)) err("template-reference-unresolved", `"${entry.id}" references stepResults.${ref}, which is not upstream`, path27, entry.id);
8041
+ if (!upstream.has(ref)) err("template-reference-unresolved", `"${entry.id}" references stepResults.${ref}, which is not upstream`, path30, entry.id);
8011
8042
  }
8012
8043
  declared.push(entry.id);
8013
8044
  break;
8014
8045
  case "sleep":
8015
- checkId(entry.id, path27);
8016
- if (!Number.isFinite(entry.duration) || entry.duration < 0) err("invalid-envelope", "sleep.duration must be a non-negative number", `${path27}.duration`, entry.id);
8046
+ checkId(entry.id, path30);
8047
+ if (!Number.isFinite(entry.duration) || entry.duration < 0) err("invalid-envelope", "sleep.duration must be a non-negative number", `${path30}.duration`, entry.id);
8017
8048
  declared.push(entry.id);
8018
8049
  break;
8019
8050
  case "sleepUntil": {
8020
8051
  const s = entry;
8021
- checkId(s.id, path27);
8022
- err("node-type-unsupported-by-engine", sleepUntilUnsupportedMessage(s.id), path27, s.id);
8052
+ checkId(s.id, path30);
8053
+ err("node-type-unsupported-by-engine", sleepUntilUnsupportedMessage(s.id), path30, s.id);
8023
8054
  if (s.date === void 0 === (s.dateFrom === void 0)) {
8024
- err("invalid-envelope", "sleepUntil needs exactly one of `date` | `dateFrom`", path27, s.id);
8055
+ err("invalid-envelope", "sleepUntil needs exactly one of `date` | `dateFrom`", path30, s.id);
8025
8056
  }
8026
8057
  if (s.dateFrom && !upstream.has(s.dateFrom.step)) {
8027
- err("sleep-until-source-missing", `sleepUntil "${s.id}" reads dateFrom.step "${s.dateFrom.step}", which is not upstream`, `${path27}.dateFrom`, s.id);
8058
+ err("sleep-until-source-missing", `sleepUntil "${s.id}" reads dateFrom.step "${s.dateFrom.step}", which is not upstream`, `${path30}.dateFrom`, s.id);
8028
8059
  }
8029
8060
  declared.push(s.id);
8030
8061
  break;
@@ -8032,10 +8063,10 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
8032
8063
  case "parallel": {
8033
8064
  const p = entry;
8034
8065
  if (p.steps.length < 2 || p.steps.length > caps.maxParallelArms) {
8035
- err("cap-exceeded", `parallel needs 2..${caps.maxParallelArms} arms (got ${p.steps.length})`, path27);
8066
+ err("cap-exceeded", `parallel needs 2..${caps.maxParallelArms} arms (got ${p.steps.length})`, path30);
8036
8067
  }
8037
8068
  p.steps.forEach((arm, j) => {
8038
- const armPath = `${path27}.steps.${j}`;
8069
+ const armPath = `${path30}.steps.${j}`;
8039
8070
  if (arm.type === "mapping") {
8040
8071
  err("mapping-placement", "a bare mapping cannot be a parallel arm \u2014 chain it as [map, step]", armPath);
8041
8072
  return;
@@ -8045,31 +8076,31 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
8045
8076
  });
8046
8077
  const executableArms = p.steps.filter(isSingleStep);
8047
8078
  const worktreeArms = executableArms.filter((a) => a.workspace?.isolation === "worktree");
8048
- if (worktreeArms.length > 0 && !p.merge) err("worktree-arms-require-merge", "worktree arms need a `merge` policy", path27);
8049
- if (worktreeArms.length === 0 && p.merge) err("merge-requires-worktree-arms", "`merge` needs at least one worktree arm", path27);
8050
- if (worktreeArms.length > WORKFLOW_JOB_MAX_WORKTREE_ARMS) err("worktree-arms-exceed-cap", `at most ${WORKFLOW_JOB_MAX_WORKTREE_ARMS} worktree arms per parallel`, path27);
8079
+ if (worktreeArms.length > 0 && !p.merge) err("worktree-arms-require-merge", "worktree arms need a `merge` policy", path30);
8080
+ if (worktreeArms.length === 0 && p.merge) err("merge-requires-worktree-arms", "`merge` needs at least one worktree arm", path30);
8081
+ if (worktreeArms.length > WORKFLOW_JOB_MAX_WORKTREE_ARMS) err("worktree-arms-exceed-cap", `at most ${WORKFLOW_JOB_MAX_WORKTREE_ARMS} worktree arms per parallel`, path30);
8051
8082
  const sharedArms = executableArms.filter((a) => {
8052
8083
  const w = workspaceOf(a);
8053
8084
  if (!w || w === "inherit" || w.isolation === "worktree") return false;
8054
8085
  return !(envelopeWorkspace?.backend === "efs" && w.mount === "ro");
8055
8086
  });
8056
- if (sharedArms.length > 1) err("workspace-parallel-requires-worktree", `${sharedArms.length} arms mount the shared workspace inside one parallel \u2014 give each arm isolation:'worktree' (+ merge) or run them sequentially`, `${path27}.steps`);
8087
+ if (sharedArms.length > 1) err("workspace-parallel-requires-worktree", `${sharedArms.length} arms mount the shared workspace inside one parallel \u2014 give each arm isolation:'worktree' (+ merge) or run them sequentially`, `${path30}.steps`);
8057
8088
  break;
8058
8089
  }
8059
8090
  case "conditional": {
8060
8091
  const c = entry;
8061
8092
  if (!Array.isArray(c.predicates) || c.predicates.length !== c.steps.length) {
8062
- err("invalid-envelope", "conditional.predicates must match conditional.steps one-to-one", path27);
8093
+ err("invalid-envelope", "conditional.predicates must match conditional.steps one-to-one", path30);
8063
8094
  }
8064
8095
  (c.predicates ?? []).forEach((p, j) => {
8065
- if (!isWellFormedPredicate(p)) err("closure-predicate", "a conditional predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path27}.predicates.${j}`);
8096
+ if (!isWellFormedPredicate(p)) err("closure-predicate", "a conditional predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path30}.predicates.${j}`);
8066
8097
  });
8067
8098
  c.steps.forEach((arm, j) => {
8068
- checkArm(arm, `${path27}.steps.${j}`, 1, "conditional");
8099
+ checkArm(arm, `${path30}.steps.${j}`, 1, "conditional");
8069
8100
  declared.push(armId(arm));
8070
8101
  });
8071
8102
  if (c.otherwise) {
8072
- checkArm(c.otherwise, `${path27}.otherwise`, 1, "conditional");
8103
+ checkArm(c.otherwise, `${path30}.otherwise`, 1, "conditional");
8073
8104
  declared.push(armId(c.otherwise));
8074
8105
  }
8075
8106
  break;
@@ -8078,34 +8109,34 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
8078
8109
  const f = entry;
8079
8110
  const o = f.opts ?? {};
8080
8111
  if (o.concurrency !== void 0 && (o.concurrency < 1 || o.concurrency > caps.maxForeachConcurrency)) {
8081
- err("cap-exceeded", `foreach.concurrency must be 1..${caps.maxForeachConcurrency}`, `${path27}.opts.concurrency`);
8112
+ err("cap-exceeded", `foreach.concurrency must be 1..${caps.maxForeachConcurrency}`, `${path30}.opts.concurrency`);
8082
8113
  }
8083
8114
  if (o.maxItems !== void 0 && (o.maxItems < 1 || o.maxItems > caps.maxForeachItems)) {
8084
- err("cap-exceeded", `foreach.maxItems must be 1..${caps.maxForeachItems} (the platform ceiling of maxForeachItems)`, `${path27}.opts.maxItems`);
8115
+ err("cap-exceeded", `foreach.maxItems must be 1..${caps.maxForeachItems} (the platform ceiling of maxForeachItems)`, `${path30}.opts.maxItems`);
8085
8116
  }
8086
8117
  {
8087
8118
  const itemsCap = o.maxItems ?? WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
8088
8119
  const literal = o.items;
8089
8120
  if (Array.isArray(literal) && literal.length > itemsCap) {
8090
- err("foreach-items-exceed-cap", `foreach({items}) carries ${literal.length} items \u2014 above maxItems ${itemsCap}`, `${path27}.opts.items`);
8121
+ err("foreach-items-exceed-cap", `foreach({items}) carries ${literal.length} items \u2014 above maxItems ${itemsCap}`, `${path30}.opts.items`);
8091
8122
  }
8092
8123
  }
8093
8124
  if (o.chunk !== void 0) {
8094
8125
  const max = o.maxItems ?? WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
8095
8126
  const size = typeof o.chunk === "number" ? o.chunk : o.chunk.size;
8096
8127
  if (!Number.isInteger(size) || size < 1 || size > max) {
8097
- err("chunk-size-invalid", `foreach.chunk.size must be an integer in [1, ${max}]`, typeof o.chunk === "number" ? `${path27}.opts.chunk` : `${path27}.opts.chunk.size`);
8128
+ err("chunk-size-invalid", `foreach.chunk.size must be an integer in [1, ${max}]`, typeof o.chunk === "number" ? `${path30}.opts.chunk` : `${path30}.opts.chunk.size`);
8098
8129
  }
8099
8130
  }
8100
8131
  if (o.rateLimit !== void 0) {
8101
8132
  const hasS = o.rateLimit.perSecond !== void 0;
8102
8133
  const hasM = o.rateLimit.perMinute !== void 0;
8103
8134
  if (hasS === hasM) {
8104
- err("rate-limit-invalid", "foreach.rateLimit needs exactly one of perSecond | perMinute", `${path27}.opts.rateLimit`);
8135
+ err("rate-limit-invalid", "foreach.rateLimit needs exactly one of perSecond | perMinute", `${path30}.opts.rateLimit`);
8105
8136
  } else if (hasS && (o.rateLimit.perSecond <= 0 || o.rateLimit.perSecond > caps.maxForeachRatePerSecond)) {
8106
- err("rate-limit-invalid", `rateLimit.perSecond must be in (0, ${caps.maxForeachRatePerSecond}]`, `${path27}.opts.rateLimit.perSecond`);
8137
+ err("rate-limit-invalid", `rateLimit.perSecond must be in (0, ${caps.maxForeachRatePerSecond}]`, `${path30}.opts.rateLimit.perSecond`);
8107
8138
  } else if (hasM && (o.rateLimit.perMinute <= 0 || o.rateLimit.perMinute > caps.maxForeachRatePerSecond * 60)) {
8108
- err("rate-limit-invalid", `rateLimit.perMinute must be in (0, ${caps.maxForeachRatePerSecond * 60}]`, `${path27}.opts.rateLimit.perMinute`);
8139
+ err("rate-limit-invalid", `rateLimit.perMinute must be in (0, ${caps.maxForeachRatePerSecond * 60}]`, `${path30}.opts.rateLimit.perMinute`);
8109
8140
  }
8110
8141
  }
8111
8142
  const bodyType = f.step.type;
@@ -8120,18 +8151,18 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
8120
8151
  const src = outputSchemas.get(d.step);
8121
8152
  arrayLike = src === void 0 ? void 0 : schemaIsArray(schemaAtPath(src, d.path ?? ""));
8122
8153
  }
8123
- if (arrayLike === false) err("foreach-items-not-array", `foreach({items}) for "${singleId(f.step)}" binds a value that is not an array`, `${path27}.step`, singleId(f.step));
8154
+ if (arrayLike === false) err("foreach-items-not-array", `foreach({items}) for "${singleId(f.step)}" binds a value that is not an array`, `${path30}.step`, singleId(f.step));
8124
8155
  }
8125
- if (bodyType === "mapping") err("container-arm-empty", "a foreach body needs a step, not a bare mapping", `${path27}.step`);
8156
+ if (bodyType === "mapping") err("container-arm-empty", "a foreach body needs a step, not a bare mapping", `${path30}.step`);
8126
8157
  else if (isHitlNode(f.step)) {
8127
8158
  if (o.chunk !== void 0) {
8128
- checkId(f.step.id, `${path27}.step`);
8129
- err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(f.step.type, f.step.id, "chunked-foreach"), `${path27}.step`, f.step.id);
8130
- } else checkHitlArm(f.step, `${path27}.step`, "foreach");
8159
+ checkId(f.step.id, `${path30}.step`);
8160
+ err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(f.step.type, f.step.id, "chunked-foreach"), `${path30}.step`, f.step.id);
8161
+ } else checkHitlArm(f.step, `${path30}.step`, "foreach");
8131
8162
  declared.push(f.step.id);
8132
8163
  } else {
8133
- checkBodyInput(f.step, `${path27}.step`, "foreach");
8134
- checkSingle(f.step, `${path27}.step`, o.chunk ? 2 : 1);
8164
+ checkBodyInput(f.step, `${path30}.step`, "foreach");
8165
+ checkSingle(f.step, `${path30}.step`, o.chunk ? 2 : 1);
8135
8166
  declared.push(singleId(f.step));
8136
8167
  }
8137
8168
  break;
@@ -8139,31 +8170,31 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
8139
8170
  case "loop": {
8140
8171
  const l = entry;
8141
8172
  if (l.maxIterations !== void 0 && (!Number.isInteger(l.maxIterations) || l.maxIterations < 1)) {
8142
- err("invalid-envelope", "loop.maxIterations must be a positive integer", `${path27}.maxIterations`);
8173
+ err("invalid-envelope", "loop.maxIterations must be a positive integer", `${path30}.maxIterations`);
8143
8174
  }
8144
8175
  if (l.intervalSeconds !== void 0 && (!Number.isInteger(l.intervalSeconds) || l.intervalSeconds < 1 || l.intervalSeconds > caps.maxLoopIntervalSeconds)) {
8145
- err("loop-interval-out-of-range", `loop.intervalSeconds must be an integer in 1..${caps.maxLoopIntervalSeconds}`, `${path27}.intervalSeconds`);
8176
+ err("loop-interval-out-of-range", `loop.intervalSeconds must be an integer in 1..${caps.maxLoopIntervalSeconds}`, `${path30}.intervalSeconds`);
8146
8177
  }
8147
- if (!isWellFormedPredicate(l.predicate)) err("closure-predicate", "a loop predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path27}.predicate`);
8178
+ if (!isWellFormedPredicate(l.predicate)) err("closure-predicate", "a loop predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path30}.predicate`);
8148
8179
  const bodyType = l.step.type;
8149
- if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path27}.step`);
8180
+ if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path30}.step`);
8150
8181
  else if (isHitlNode(l.step)) {
8151
- checkHitlArm(l.step, `${path27}.step`, "loop");
8182
+ checkHitlArm(l.step, `${path30}.step`, "loop");
8152
8183
  declared.push(l.step.id);
8153
8184
  } else {
8154
- checkBodyInput(l.step, `${path27}.step`, "loop");
8155
- checkSingle(l.step, `${path27}.step`, 1);
8185
+ checkBodyInput(l.step, `${path30}.step`, "loop");
8186
+ checkSingle(l.step, `${path30}.step`, 1);
8156
8187
  declared.push(singleId(l.step));
8157
8188
  }
8158
8189
  break;
8159
8190
  }
8160
8191
  case "approval":
8161
8192
  case "waitForSignal":
8162
- checkHitl(entry, path27);
8193
+ checkHitl(entry, path30);
8163
8194
  declared.push(entry.id);
8164
8195
  break;
8165
8196
  default:
8166
- err("invalid-envelope", `unknown entry type "${entry.type}"`, path27);
8197
+ err("invalid-envelope", `unknown entry type "${entry.type}"`, path30);
8167
8198
  }
8168
8199
  for (const id2 of declared) upstream.add(id2);
8169
8200
  });
@@ -8412,11 +8443,11 @@ function compilePlan(g) {
8412
8443
  }
8413
8444
  function resolvePath(rawPath, ctx) {
8414
8445
  const templateMatch = PATH_PLACEHOLDER.exec(rawPath.trim());
8415
- const path27 = templateMatch ? templateMatch[1].trim() : rawPath.trim();
8416
- if (path27 === "") return MISSING;
8417
- const dot = path27.indexOf(".");
8418
- const scope = dot === -1 ? path27 : path27.slice(0, dot);
8419
- const rest = dot === -1 ? "" : path27.slice(dot + 1);
8446
+ const path30 = templateMatch ? templateMatch[1].trim() : rawPath.trim();
8447
+ if (path30 === "") return MISSING;
8448
+ const dot = path30.indexOf(".");
8449
+ const scope = dot === -1 ? path30 : path30.slice(0, dot);
8450
+ const rest = dot === -1 ? "" : path30.slice(dot + 1);
8420
8451
  let root;
8421
8452
  switch (scope) {
8422
8453
  case "initData":
@@ -8450,9 +8481,9 @@ function resolvePath(rawPath, ctx) {
8450
8481
  }
8451
8482
  return walk(root, rest);
8452
8483
  }
8453
- function walk(root, path27) {
8454
- if (path27 === "") return root;
8455
- const parts = path27.split(".");
8484
+ function walk(root, path30) {
8485
+ if (path30 === "") return root;
8486
+ const parts = path30.split(".");
8456
8487
  let value22 = root;
8457
8488
  for (const part of parts) {
8458
8489
  if (value22 === null || value22 === void 0) return MISSING;
@@ -8578,14 +8609,14 @@ function step(s) {
8578
8609
  function stepOf(id2) {
8579
8610
  return step(id2);
8580
8611
  }
8581
- function init(path27) {
8612
+ function init(path30) {
8582
8613
  return {
8583
- path: path27 === "" ? "initData" : `initData.${path27}`
8614
+ path: path30 === "" ? "initData" : `initData.${path30}`
8584
8615
  };
8585
8616
  }
8586
- function state(path27) {
8617
+ function state(path30) {
8587
8618
  return {
8588
- path: path27 === "" ? "state" : `state.${path27}`
8619
+ path: path30 === "" ? "state" : `state.${path30}`
8589
8620
  };
8590
8621
  }
8591
8622
  function lit(v) {
@@ -9515,7 +9546,7 @@ function timeZoneSupported(tz) {
9515
9546
  return false;
9516
9547
  }
9517
9548
  }
9518
- function validateBusinessHours(cal, path27 = "businessHours") {
9549
+ function validateBusinessHours(cal, path30 = "businessHours") {
9519
9550
  const issues = [];
9520
9551
  const issue = /* @__PURE__ */ __name4((p, message) => issues.push({
9521
9552
  code: "business-hours-invalid",
@@ -9525,32 +9556,32 @@ function validateBusinessHours(cal, path27 = "businessHours") {
9525
9556
  if (typeof cal !== "object" || cal === null) return [
9526
9557
  {
9527
9558
  code: "business-hours-invalid",
9528
- path: path27,
9559
+ path: path30,
9529
9560
  message: "not an object"
9530
9561
  }
9531
9562
  ];
9532
9563
  const c = cal;
9533
- if (!timeZoneSupported(c.tz)) issue(`${path27}.tz`, "unknown IANA time zone");
9564
+ if (!timeZoneSupported(c.tz)) issue(`${path30}.tz`, "unknown IANA time zone");
9534
9565
  if (c.calendar === void 0 || c.calendar === "mon-fri") return issues;
9535
9566
  if (typeof c.calendar !== "object" || c.calendar === null) {
9536
- issue(`${path27}.calendar`, "expected 'mon-fri' or { days, start, end, holidays? }");
9567
+ issue(`${path30}.calendar`, "expected 'mon-fri' or { days, start, end, holidays? }");
9537
9568
  return issues;
9538
9569
  }
9539
9570
  const k = c.calendar;
9540
9571
  if (!Array.isArray(k.days) || k.days.length === 0 || k.days.some((d) => !Number.isInteger(d) || d < 0 || d > 6)) {
9541
- issue(`${path27}.calendar.days`, "days must be a non-empty array of 0\u20136");
9572
+ issue(`${path30}.calendar.days`, "days must be a non-empty array of 0\u20136");
9542
9573
  }
9543
9574
  const startOk = typeof k.start === "string" && HHMM.test(k.start);
9544
9575
  const endOk = typeof k.end === "string" && HHMM.test(k.end);
9545
- if (!startOk) issue(`${path27}.calendar.start`, "expected 'HH:MM'");
9546
- if (!endOk) issue(`${path27}.calendar.end`, "expected 'HH:MM'");
9547
- if (startOk && endOk && toMinutes(k.start) >= toMinutes(k.end)) issue(`${path27}.calendar`, "start must be before end");
9576
+ if (!startOk) issue(`${path30}.calendar.start`, "expected 'HH:MM'");
9577
+ if (!endOk) issue(`${path30}.calendar.end`, "expected 'HH:MM'");
9578
+ if (startOk && endOk && toMinutes(k.start) >= toMinutes(k.end)) issue(`${path30}.calendar`, "start must be before end");
9548
9579
  if (k.holidays !== void 0) {
9549
- if (!Array.isArray(k.holidays)) issue(`${path27}.calendar.holidays`, "expected an array of YYYY-MM-DD");
9580
+ if (!Array.isArray(k.holidays)) issue(`${path30}.calendar.holidays`, "expected an array of YYYY-MM-DD");
9550
9581
  else {
9551
- if (k.holidays.length > MAX_HOLIDAYS) issue(`${path27}.calendar.holidays`, `at most ${MAX_HOLIDAYS} holidays`);
9582
+ if (k.holidays.length > MAX_HOLIDAYS) issue(`${path30}.calendar.holidays`, `at most ${MAX_HOLIDAYS} holidays`);
9552
9583
  k.holidays.forEach((h, i) => {
9553
- if (typeof h !== "string" || !YMD.test(h)) issue(`${path27}.calendar.holidays[${i}]`, "expected 'YYYY-MM-DD'");
9584
+ if (typeof h !== "string" || !YMD.test(h)) issue(`${path30}.calendar.holidays[${i}]`, "expected 'YYYY-MM-DD'");
9554
9585
  });
9555
9586
  }
9556
9587
  }
@@ -9962,7 +9993,7 @@ function rebaseItemPointer(pointer, itemsPath, index) {
9962
9993
  const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
9963
9994
  return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
9964
9995
  }
9965
- function validateWorkflowSchedule(schedule, path27 = "/schedule") {
9996
+ function validateWorkflowSchedule(schedule, path30 = "/schedule") {
9966
9997
  if (schedule === void 0 || schedule === null) return [];
9967
9998
  const issue = /* @__PURE__ */ __name4((at, detail) => [
9968
9999
  {
@@ -9973,40 +10004,40 @@ function validateWorkflowSchedule(schedule, path27 = "/schedule") {
9973
10004
  }
9974
10005
  ], "issue");
9975
10006
  if (!isObject(schedule)) {
9976
- return issue(path27, `\`schedule\` is ${Array.isArray(schedule) ? "an array" : `a ${typeof schedule}`}, not a typed schedule object`);
10007
+ return issue(path30, `\`schedule\` is ${Array.isArray(schedule) ? "an array" : `a ${typeof schedule}`}, not a typed schedule object`);
9977
10008
  }
9978
10009
  const type = schedule.type;
9979
10010
  if (type === void 0) {
9980
10011
  const keys = Object.keys(schedule);
9981
10012
  const seen = keys.length ? ` (got { ${keys.join(", ")} })` : " (got {})";
9982
- return issue(path27, `\`schedule\` carries no \`type\` discriminator${seen}`);
10013
+ return issue(path30, `\`schedule\` carries no \`type\` discriminator${seen}`);
9983
10014
  }
9984
10015
  if (typeof type !== "string" || !WORKFLOW_SCHEDULE_TYPES.includes(type)) {
9985
- return issue(path27, `\`schedule.type\` ${JSON.stringify(type)} is not one of ${WORKFLOW_SCHEDULE_TYPES.map((t) => `'${t}'`).join(" | ")}`);
10016
+ return issue(path30, `\`schedule.type\` ${JSON.stringify(type)} is not one of ${WORKFLOW_SCHEDULE_TYPES.map((t) => `'${t}'`).join(" | ")}`);
9986
10017
  }
9987
10018
  if (schedule.runAs !== void 0 && !WORKFLOW_SCHEDULE_RUN_AS.includes(schedule.runAs)) {
9988
- return issue(`${path27}/runAs`, `\`schedule.runAs\` ${JSON.stringify(schedule.runAs)} is not one of ${WORKFLOW_SCHEDULE_RUN_AS.map((v) => `'${v}'`).join(" | ")}`);
10019
+ return issue(`${path30}/runAs`, `\`schedule.runAs\` ${JSON.stringify(schedule.runAs)} is not one of ${WORKFLOW_SCHEDULE_RUN_AS.map((v) => `'${v}'`).join(" | ")}`);
9989
10020
  }
9990
10021
  switch (type) {
9991
10022
  case "cron": {
9992
10023
  if (typeof schedule.expression !== "string" || schedule.expression.trim().length === 0) {
9993
- return issue(`${path27}/expression`, "a { type: 'cron' } schedule needs a non-empty string `expression`");
10024
+ return issue(`${path30}/expression`, "a { type: 'cron' } schedule needs a non-empty string `expression`");
9994
10025
  }
9995
10026
  if (schedule.timezone !== void 0 && (typeof schedule.timezone !== "string" || schedule.timezone.length === 0)) {
9996
- return issue(`${path27}/timezone`, "a { type: 'cron' } schedule's `timezone`, when given, is a non-empty IANA string");
10027
+ return issue(`${path30}/timezone`, "a { type: 'cron' } schedule's `timezone`, when given, is a non-empty IANA string");
9997
10028
  }
9998
10029
  return [];
9999
10030
  }
10000
10031
  case "interval": {
10001
10032
  const s = schedule.seconds;
10002
10033
  if (typeof s !== "number" || !Number.isFinite(s) || s <= 0) {
10003
- return issue(`${path27}/seconds`, "a { type: 'interval' } schedule needs a positive number `seconds`");
10034
+ return issue(`${path30}/seconds`, "a { type: 'interval' } schedule needs a positive number `seconds`");
10004
10035
  }
10005
10036
  return [];
10006
10037
  }
10007
10038
  case "once": {
10008
10039
  if (typeof schedule.executeAt !== "string" || Number.isNaN(Date.parse(schedule.executeAt))) {
10009
- return issue(`${path27}/executeAt`, "a { type: 'once' } schedule needs an ISO-8601 string `executeAt`");
10040
+ return issue(`${path30}/executeAt`, "a { type: 'once' } schedule needs an ISO-8601 string `executeAt`");
10010
10041
  }
10011
10042
  return [];
10012
10043
  }
@@ -10449,15 +10480,15 @@ var init_dist2 = __esm({
10449
10480
  __name4(resolveDescriptor, "resolveDescriptor");
10450
10481
  __name(resolveMapping, "resolveMapping");
10451
10482
  __name4(resolveMapping, "resolveMapping");
10452
- fromInit = /* @__PURE__ */ __name4((path27) => ({
10483
+ fromInit = /* @__PURE__ */ __name4((path30) => ({
10453
10484
  initData: true,
10454
- path: path27
10485
+ path: path30
10455
10486
  }), "fromInit");
10456
- fromStep = /* @__PURE__ */ __name4((s, path27 = "") => {
10487
+ fromStep = /* @__PURE__ */ __name4((s, path30 = "") => {
10457
10488
  const idOf = /* @__PURE__ */ __name4((x) => typeof x === "string" ? x : x.id, "idOf");
10458
10489
  return {
10459
10490
  step: Array.isArray(s) ? s.map(idOf) : idOf(s),
10460
- path: path27
10491
+ path: path30
10461
10492
  };
10462
10493
  }, "fromStep");
10463
10494
  value = /* @__PURE__ */ __name4((v) => ({
@@ -10466,12 +10497,12 @@ var init_dist2 = __esm({
10466
10497
  template = /* @__PURE__ */ __name4((s) => ({
10467
10498
  template: s
10468
10499
  }), "template");
10469
- fromRequest = /* @__PURE__ */ __name4((path27) => ({
10470
- requestContextPath: path27
10500
+ fromRequest = /* @__PURE__ */ __name4((path30) => ({
10501
+ requestContextPath: path30
10471
10502
  }), "fromRequest");
10472
- rows = /* @__PURE__ */ __name4((s, path27, page) => ({
10503
+ rows = /* @__PURE__ */ __name4((s, path30, page) => ({
10473
10504
  step: typeof s === "string" ? s : s.id,
10474
- path: path27,
10505
+ path: path30,
10475
10506
  rows: page
10476
10507
  }), "rows");
10477
10508
  fromKnowledge = /* @__PURE__ */ __name4((k) => ({
@@ -11474,13 +11505,13 @@ var init_workflow = __esm({
11474
11505
  }
11475
11506
  if ("step" in items && Array.isArray(items.step)) throw foreachItemsNotLowered("a fan-in fromStep([\u2026])");
11476
11507
  if (typeof items.path !== "string") throw foreachItemsNotLowered("value(\u2026) / template(\u2026) / fromRequest(\u2026) / fromKnowledge(\u2026)");
11477
- const path27 = items.path;
11478
- if (path27.startsWith("initData")) return {
11508
+ const path30 = items.path;
11509
+ if (path30.startsWith("initData")) return {
11479
11510
  initData: true,
11480
- path: path27 === "initData" ? "" : path27.slice("initData.".length)
11511
+ path: path30 === "initData" ? "" : path30.slice("initData.".length)
11481
11512
  };
11482
- if (path27.startsWith("stepResults.")) {
11483
- const rest = path27.slice("stepResults.".length);
11513
+ if (path30.startsWith("stepResults.")) {
11514
+ const rest = path30.slice("stepResults.".length);
11484
11515
  const dot = rest.indexOf(".");
11485
11516
  return dot === -1 ? {
11486
11517
  step: rest,
@@ -11490,7 +11521,7 @@ var init_workflow = __esm({
11490
11521
  path: rest.slice(dot + 1)
11491
11522
  };
11492
11523
  }
11493
- throw new LuaWorkflowBuildError("invalid-envelope", `foreach.items ref "${path27}" must point at initData.* or stepResults.*`);
11524
+ throw new LuaWorkflowBuildError("invalid-envelope", `foreach.items ref "${path30}" must point at initData.* or stepResults.*`);
11494
11525
  }, "refToDescriptor");
11495
11526
  __workflowCommitHook = {};
11496
11527
  LuaWorkflow = class {
@@ -12980,8 +13011,8 @@ function buildSourceArchive(files) {
12980
13011
  map[entryFile] = source;
12981
13012
  }
12982
13013
  if (Object.keys(map).length === 0) return null;
12983
- const json = JSON.stringify(map);
12984
- const gz = zlib.gzipSync(Buffer.from(json, "utf-8"));
13014
+ const json2 = JSON.stringify(map);
13015
+ const gz = zlib.gzipSync(Buffer.from(json2, "utf-8"));
12985
13016
  return gz.toString("base64");
12986
13017
  }
12987
13018
  function getPrimitivesByKind(manifest, kind) {
@@ -13450,12 +13481,12 @@ var init_dist3 = __esm({
13450
13481
  this.options = options;
13451
13482
  this.fetchFn = options.fetch ?? fetch;
13452
13483
  }
13453
- url(path27) {
13484
+ url(path30) {
13454
13485
  const base = this.options.baseUrl.replace(/\/$/, "");
13455
- return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path27}`;
13486
+ return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path30}`;
13456
13487
  }
13457
- async json(method, path27, body) {
13458
- const endpoint = this.url(path27);
13488
+ async json(method, path30, body) {
13489
+ const endpoint = this.url(path30);
13459
13490
  const res = await this.fetchFn(endpoint, {
13460
13491
  method,
13461
13492
  headers: {
@@ -13474,7 +13505,7 @@ var init_dist3 = __esm({
13474
13505
  } catch {
13475
13506
  text2 = void 0;
13476
13507
  }
13477
- throw new BackupHttpError(`Backup request failed: ${method} ${path27} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
13508
+ throw new BackupHttpError(`Backup request failed: ${method} ${path30} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
13478
13509
  }
13479
13510
  const text = await res.text();
13480
13511
  if (!text) return void 0;
@@ -13621,12 +13652,12 @@ var init_backup_api_service = __esm({
13621
13652
  * @returns Promise resolving to missing/existing hash lists
13622
13653
  */
13623
13654
  async checkBlobsExist(hashes) {
13624
- const path27 = `/developer/agents/${this.agentId}/backup/check-blobs`;
13655
+ const path30 = `/developer/agents/${this.agentId}/backup/check-blobs`;
13625
13656
  if (hashes.length <= CHECK_BLOBS_MAX_HASHES) {
13626
13657
  const data = {
13627
13658
  hashes
13628
13659
  };
13629
- return this.httpPost(path27, data, {});
13660
+ return this.httpPost(path30, data, {});
13630
13661
  }
13631
13662
  const merged = {
13632
13663
  missing: [],
@@ -13638,7 +13669,7 @@ var init_backup_api_service = __esm({
13638
13669
  const data = {
13639
13670
  hashes: hashes.slice(i, i + CHECK_BLOBS_MAX_HASHES)
13640
13671
  };
13641
- const part = await this.httpPost(path27, data, {});
13672
+ const part = await this.httpPost(path30, data, {});
13642
13673
  if (!part.success || !part.data) return part;
13643
13674
  merged.missing.push(...part.data.missing);
13644
13675
  merged.existing.push(...part.data.existing);
@@ -14704,7 +14735,8 @@ var init_skill_handler = __esm({
14704
14735
  }
14705
14736
  return {
14706
14737
  name: skill.name,
14707
- description: skill.description,
14738
+ // Same fallback as createOnServer — the server's version row requires a description.
14739
+ description: skill.description || `A Lua skill for ${skill.name}`,
14708
14740
  // Same gate as createOnServer — server validator rejects empty content.
14709
14741
  ...hasPersonaTextContent(skill.context) ? {
14710
14742
  context: skill.context
@@ -15236,14 +15268,14 @@ async function withErrorHandling(commandFn, commandName, opts) {
15236
15268
  } catch {
15237
15269
  }
15238
15270
  }
15239
- let json = false;
15271
+ let json2 = false;
15240
15272
  try {
15241
- json = typeof opts?.json === "function" ? !!opts.json() : !!opts?.json;
15273
+ json2 = typeof opts?.json === "function" ? !!opts.json() : !!opts?.json;
15242
15274
  } catch {
15243
15275
  }
15244
15276
  const reported = reportCliError(error, {
15245
15277
  extraHint,
15246
- json
15278
+ json: json2
15247
15279
  });
15248
15280
  process.exitCode = reported.exitCode;
15249
15281
  await showUpdateWarningIfNeeded(versionCheckPromise);
@@ -20709,8 +20741,8 @@ var init_voice_plugin = __esm({
20709
20741
  }, "isMissingTopLevelRequired");
20710
20742
  for (const issue of parsed.error.issues) {
20711
20743
  if (isMissingTopLevelRequired(issue)) continue;
20712
- const path27 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
20713
- errors.push(validationError(`Voice config invalid at \`${path27}\`: ${issue.message}`, {
20744
+ const path30 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
20745
+ errors.push(validationError(`Voice config invalid at \`${path30}\`: ${issue.message}`, {
20714
20746
  line
20715
20747
  }));
20716
20748
  }
@@ -22527,7 +22559,7 @@ function parseWorkflowScript(script, fileName) {
22527
22559
  status: "deferred"
22528
22560
  });
22529
22561
  }, "collectRole");
22530
- const walk3 = /* @__PURE__ */ __name((node) => {
22562
+ const walk4 = /* @__PURE__ */ __name((node) => {
22531
22563
  if (node === metaStatement) return;
22532
22564
  if (ts4.isIdentifier(node)) {
22533
22565
  const parent = node.parent;
@@ -22593,15 +22625,15 @@ function parseWorkflowScript(script, fileName) {
22593
22625
  issues.push(errorAt(sf, file, t, "SCRIPT_META_INVALID", `step() timeoutSeconds must be a literal 1..${WORKFLOW_SCRIPT_STEP_MAX_TIMEOUT_SECONDS}`, "step-timeout-invalid"));
22594
22626
  }
22595
22627
  }
22596
- if (label2) walk3(label2);
22597
- if (stepOpts) walk3(stepOpts);
22628
+ if (label2) walk4(label2);
22629
+ if (stepOpts) walk4(stepOpts);
22598
22630
  return;
22599
22631
  }
22600
22632
  }
22601
22633
  }
22602
- ts4.forEachChild(node, walk3);
22634
+ ts4.forEachChild(node, walk4);
22603
22635
  }, "walk");
22604
- for (const st of sf.statements) walk3(st);
22636
+ for (const st of sf.statements) walk4(st);
22605
22637
  return {
22606
22638
  meta,
22607
22639
  issues,
@@ -24265,7 +24297,7 @@ function findConfig(sourceFile, exportName) {
24265
24297
  }
24266
24298
  return argOf(sourceFile.getVariableDeclaration(exportName)?.getInitializer());
24267
24299
  }
24268
- var WEB_APP_NAME_PATTERN, ROUTE_KEY_PATTERN, MAX_ROUTES, SCHEMA_PARTS, LUA_CLI_SPECIFIERS, INJECTED_HELPERS, schemaKey, WebAppPlugin;
24300
+ var ROUTE_KEY_PATTERN, SCHEMA_PARTS, LUA_CLI_SPECIFIERS, INJECTED_HELPERS, schemaKey, WebAppPlugin;
24269
24301
  var init_web_app_plugin = __esm({
24270
24302
  "src/compiler/plugins/web-app.plugin.ts"() {
24271
24303
  "use strict";
@@ -24273,9 +24305,7 @@ var init_web_app_plugin = __esm({
24273
24305
  init_base();
24274
24306
  init_ast_helpers();
24275
24307
  init_schema_converter();
24276
- WEB_APP_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
24277
24308
  ROUTE_KEY_PATTERN = new RegExp(`^(${WEB_ROUTE_METHODS.join("|")}) (/\\S*)$`);
24278
- MAX_ROUTES = 200;
24279
24309
  SCHEMA_PARTS = [
24280
24310
  "params",
24281
24311
  "query",
@@ -24388,8 +24418,8 @@ var init_web_app_plugin = __esm({
24388
24418
  errors.push(validationError(`Web app route key ${JSON.stringify(r.key)} must look like 'GET /path' (${WEB_ROUTE_METHODS.join(", ")})`, at));
24389
24419
  }
24390
24420
  }
24391
- if (meta.routes.length > MAX_ROUTES) {
24392
- errors.push(validationError(`Web app declares ${meta.routes.length} routes; the limit is ${MAX_ROUTES}`, at));
24421
+ if (meta.routes.length > WEB_APP_MAX_ROUTES) {
24422
+ errors.push(validationError(`Web app declares ${meta.routes.length} routes; the limit is ${WEB_APP_MAX_ROUTES}`, at));
24393
24423
  }
24394
24424
  if (meta.routes.length === 0 && !meta.hasFetch && meta.unreadableRoutes.length === 0) {
24395
24425
  errors.push(validationError("Web app must declare at least one route or a `fetch` handler", at));
@@ -25390,9 +25420,9 @@ function packageJsonHasWorkspaces(pkgPath) {
25390
25420
  if (!fileExists(pkgPath)) return false;
25391
25421
  try {
25392
25422
  const raw = fs9.readFileSync(pkgPath, "utf-8");
25393
- const json = JSON.parse(raw);
25394
- if (!json.workspaces) return false;
25395
- return Array.isArray(json.workspaces) || typeof json.workspaces === "object";
25423
+ const json2 = JSON.parse(raw);
25424
+ if (!json2.workspaces) return false;
25425
+ return Array.isArray(json2.workspaces) || typeof json2.workspaces === "object";
25396
25426
  } catch {
25397
25427
  return false;
25398
25428
  }
@@ -29023,8 +29053,8 @@ var init_developer_api_service = __esm({
29023
29053
  * @returns Promise resolving to an ApiResponse containing the profile, or null if not found
29024
29054
  */
29025
29055
  async getUserProfileByEmail(email, agentId) {
29026
- const path27 = `/developer/user/profile/email/${encodeURIComponent(email)}`;
29027
- const scopedPath = agentId ? `${path27}?agentId=${encodeURIComponent(agentId)}` : path27;
29056
+ const path30 = `/developer/user/profile/email/${encodeURIComponent(email)}`;
29057
+ const scopedPath = agentId ? `${path30}?agentId=${encodeURIComponent(agentId)}` : path30;
29028
29058
  return this.httpGet(scopedPath, {});
29029
29059
  }
29030
29060
  /**
@@ -29034,8 +29064,8 @@ var init_developer_api_service = __esm({
29034
29064
  */
29035
29065
  async getUserProfileByPhone(phone, agentId) {
29036
29066
  const normalizedPhone = phone.replace(/^\+/, "");
29037
- const path27 = `/developer/user/profile/phone/${normalizedPhone}`;
29038
- const scopedPath = agentId ? `${path27}?agentId=${encodeURIComponent(agentId)}` : path27;
29067
+ const path30 = `/developer/user/profile/phone/${normalizedPhone}`;
29068
+ const scopedPath = agentId ? `${path30}?agentId=${encodeURIComponent(agentId)}` : path30;
29039
29069
  return this.httpGet(scopedPath, {});
29040
29070
  }
29041
29071
  };
@@ -31753,6 +31783,29 @@ var AgentApi = class extends HttpClient {
31753
31783
  async createSlackChannel(agentId, channelData) {
31754
31784
  return this.httpPost(`/admin/agents/${agentId}/channels`, channelData, {});
31755
31785
  }
31786
+ /** The Slack manifest for this agent: a new app's create link, or with `channelId` the update for that app. */
31787
+ async getSlackManifest(agentId, channelId) {
31788
+ const query = channelId ? `?channelId=${encodeURIComponent(channelId)}` : "";
31789
+ return this.httpGet(`/admin/agents/${agentId}/channels/slack/manifest${query}`, {});
31790
+ }
31791
+ /** A signed install link for a public Slack connection; the installer lands on Lua's result page. */
31792
+ async getSlackInstallUrl(agentId, channelId) {
31793
+ return this.httpPost(`/admin/agents/${agentId}/channels/${channelId}/slack/install-url`, {
31794
+ origin: "cli"
31795
+ }, {});
31796
+ }
31797
+ /** Asks Slack what the connection's token may do now; a dead token marks it uninstalled. */
31798
+ async checkSlackConnection(agentId, channelId) {
31799
+ return this.httpPost(`/admin/agents/${agentId}/channels/${channelId}/slack/check`, {}, {});
31800
+ }
31801
+ /** Saves the app's signing secret on every connection of this agent to the app. */
31802
+ async saveSlackSigningSecret(agentId, channelIdentifier, signingSecret) {
31803
+ return this.httpPut(`/admin/agents/${agentId}/channels/${encodeURIComponent(channelIdentifier)}`, {
31804
+ slack: {
31805
+ signingSecret
31806
+ }
31807
+ }, {});
31808
+ }
31756
31809
  /**
31757
31810
  * Creates a new Email channel for a specific agent
31758
31811
  * @param agentId - The unique identifier of the agent
@@ -34666,7 +34719,7 @@ __name(apiKeyCommand, "apiKeyCommand");
34666
34719
  init_cli();
34667
34720
  init_files();
34668
34721
  init_request_credential();
34669
- import path23 from "path";
34722
+ import path25 from "path";
34670
34723
 
34671
34724
  // src/commands/sync.ts
34672
34725
  init_dist();
@@ -36749,8 +36802,8 @@ function buildSupervisionAgentsApi(call) {
36749
36802
  }), "persona"),
36750
36803
  source: /* @__PURE__ */ __name7((agentId) => ({
36751
36804
  list: /* @__PURE__ */ __name7(() => call("source.list", agentId), "list"),
36752
- get: /* @__PURE__ */ __name7((path27) => call("source.get", agentId, {
36753
- path: path27
36805
+ get: /* @__PURE__ */ __name7((path30) => call("source.get", agentId, {
36806
+ path: path30
36754
36807
  }), "get")
36755
36808
  }), "source"),
36756
36809
  /** Environment variable NAMES only — values are never returned. */
@@ -36762,6 +36815,211 @@ function buildSupervisionAgentsApi(call) {
36762
36815
  __name(buildSupervisionAgentsApi, "buildSupervisionAgentsApi");
36763
36816
  __name7(buildSupervisionAgentsApi, "buildSupervisionAgentsApi");
36764
36817
 
36818
+ // ../sandbox-runtime/dist/web-route-wrapper.mjs
36819
+ init_dist();
36820
+ var __defProp8 = Object.defineProperty;
36821
+ var __name8 = /* @__PURE__ */ __name((target, value3) => __defProp8(target, "name", { value: value3, configurable: true }), "__name");
36822
+ var WEB_ROUTE_WRAPPER_FUNCTION_SOURCE = `async function (input, primitive, logTiming, console) {
36823
+ const RESPONSE_HEADER_ALLOWLIST = ${JSON.stringify(WEB_ROUTE_RESPONSE_HEADER_ALLOWLIST)};
36824
+ const MAX_RESPONSE_BYTES = ${WEB_ROUTE_MAX_RESPONSE_BYTES};
36825
+ const startTime = Date.now();
36826
+ const timing = typeof logTiming === 'function' ? logTiming : async () => {};
36827
+ const req = input.request;
36828
+
36829
+ // Buffer and URLSearchParams are sandbox contract globals (shared-sandbox contract.ts).
36830
+ const encodeBase64 = (bytes) => Buffer.from(bytes).toString('base64');
36831
+ const decodeBase64 = (text) => new Uint8Array(Buffer.from(text, 'base64'));
36832
+ const jsonResult = (status, value) => ({ status, headers: { 'content-type': 'application/json' }, body: { kind: 'json', value } });
36833
+
36834
+ const bodyValue = req.body ? req.body.value : undefined;
36835
+ const rawBodyForRequest = () => {
36836
+ if (!req.body || req.method === 'GET') return undefined;
36837
+ if (req.body.kind === 'json') return JSON.stringify(req.body.value);
36838
+ if (req.body.kind === 'text') return req.body.value;
36839
+ return decodeBase64(req.body.value);
36840
+ };
36841
+
36842
+ // Built on first use: typed handlers get the parsed body and rarely read ctx.request,
36843
+ // so the body is not re-serialized (or base64-decoded) for them.
36844
+ let webRequest;
36845
+ const getRequest = () => {
36846
+ if (webRequest === undefined && typeof Request === 'function' && typeof URL === 'function') {
36847
+ const url = new URL(req.path, input.app.origin);
36848
+ for (const key of Object.keys(req.query || {})) {
36849
+ const v = req.query[key];
36850
+ for (const one of Array.isArray(v) ? v : [v]) url.searchParams.append(key, String(one));
36851
+ }
36852
+ webRequest = new Request(url, { method: req.method, headers: req.headers || {}, body: rawBodyForRequest() });
36853
+ }
36854
+ return webRequest;
36855
+ };
36856
+
36857
+ // The contract's response cap, enforced here so an oversized body is refused before it
36858
+ // is materialized (and base64-inflated) in the sandbox; the gateway re-checks the bytes.
36859
+ const tooLarge = () => jsonResult(500, { code: 'RESPONSE_TOO_LARGE', message: 'Response exceeds ' + MAX_RESPONSE_BYTES + ' bytes' });
36860
+
36861
+ // Reads a Response body up to the cap: a stream is cancelled as soon as it passes it.
36862
+ const readCapped = async (response) => {
36863
+ if (response.body && typeof response.body.getReader === 'function') {
36864
+ const reader = response.body.getReader();
36865
+ const chunks = [];
36866
+ let total = 0;
36867
+ for (;;) {
36868
+ const { done, value } = await reader.read();
36869
+ if (done) break;
36870
+ total += value.byteLength;
36871
+ if (total > MAX_RESPONSE_BYTES) {
36872
+ try { await reader.cancel(); } catch (e) {}
36873
+ return null;
36874
+ }
36875
+ chunks.push(value);
36876
+ }
36877
+ const out = new Uint8Array(total);
36878
+ let offset = 0;
36879
+ for (const c of chunks) { out.set(c, offset); offset += c.byteLength; }
36880
+ return out;
36881
+ }
36882
+ const bytes = new Uint8Array(await response.arrayBuffer());
36883
+ return bytes.length > MAX_RESPONSE_BYTES ? null : bytes;
36884
+ };
36885
+
36886
+ // type/subtype without parameters, lower-cased.
36887
+ const mediaTypeOf = (contentType) => String(contentType || '').split(';')[0].trim().toLowerCase();
36888
+ const isJsonType = (t) => t === 'application/json' || t.endsWith('+json');
36889
+ const isTextType = (t) =>
36890
+ t.startsWith('text/') || t.endsWith('+xml') || t === 'application/xml' || t === 'application/javascript' || t === 'application/ecmascript';
36891
+
36892
+ const normalize = async (result) => {
36893
+ if (result === undefined || result === null) return { status: 204, headers: {} };
36894
+ const looksLikeResponse =
36895
+ typeof result === 'object' && typeof result.status === 'number' && result.headers && typeof result.headers.get === 'function' && typeof result.arrayBuffer === 'function';
36896
+ if (!looksLikeResponse) {
36897
+ // UTF-16 length never exceeds the UTF-8 byte length, so this under-counts at worst;
36898
+ // the gateway applies the exact byte cap.
36899
+ const serialized = JSON.stringify(result);
36900
+ if (serialized !== undefined && serialized.length > MAX_RESPONSE_BYTES) return tooLarge();
36901
+ return jsonResult(200, result);
36902
+ }
36903
+ const headers = {};
36904
+ result.headers.forEach((value, name) => {
36905
+ const n = String(name).toLowerCase();
36906
+ if (n === 'set-cookie') return;
36907
+ if (RESPONSE_HEADER_ALLOWLIST.includes(n) || n.startsWith('x-lua-')) headers[n] = value;
36908
+ });
36909
+ const out = { status: result.status, headers };
36910
+ if (result.status === 204 || result.status === 304) return out;
36911
+ const mediaType = mediaTypeOf(headers['content-type']);
36912
+ const bytes = await readCapped(result);
36913
+ if (bytes === null) return tooLarge();
36914
+ if (bytes.length === 0) return out;
36915
+ if (isJsonType(mediaType)) {
36916
+ const text = new TextDecoder().decode(bytes);
36917
+ try { out.body = { kind: 'json', value: JSON.parse(text) }; } catch (e) { out.body = { kind: 'text', value: text }; }
36918
+ return out;
36919
+ }
36920
+ if (isTextType(mediaType)) {
36921
+ out.body = { kind: 'text', value: new TextDecoder().decode(bytes) };
36922
+ return out;
36923
+ }
36924
+ out.body = { kind: 'base64', value: encodeBase64(bytes) };
36925
+ return out;
36926
+ };
36927
+
36928
+ const validationFailure = (part, issues) => jsonResult(400, { code: 'VALIDATION', message: 'Invalid ' + part, issues });
36929
+
36930
+ await timing('start', 'Web route started');
36931
+ try {
36932
+ const def = req.route && primitive.routes && typeof primitive.routes === 'object' ? primitive.routes[req.route] : undefined;
36933
+ const handler = typeof def === 'function' ? def : def && typeof def.handler === 'function' ? def.handler : undefined;
36934
+ let result;
36935
+ if (!handler) {
36936
+ const request = typeof primitive.fetch === 'function' ? getRequest() : undefined;
36937
+ if (request) {
36938
+ result = await normalize(await primitive.fetch(request));
36939
+ } else {
36940
+ result = jsonResult(404, { code: 'NOT_FOUND', message: 'No route handler for ' + (req.route || req.method + ' ' + req.path) });
36941
+ }
36942
+ } else {
36943
+ const parsed = { params: req.params || {}, query: req.query || {}, body: bodyValue };
36944
+ const schemas = typeof def === 'object' && def ? def : {};
36945
+ for (const part of ['params', 'query', 'body']) {
36946
+ const schema = schemas[part];
36947
+ if (schema && typeof schema.safeParse === 'function') {
36948
+ const r = schema.safeParse(parsed[part]);
36949
+ if (!r.success) {
36950
+ const issues = r.error && Array.isArray(r.error.issues) ? r.error.issues : r.error;
36951
+ result = validationFailure(part, issues);
36952
+ break;
36953
+ }
36954
+ parsed[part] = r.data;
36955
+ }
36956
+ }
36957
+ if (!result) {
36958
+ const ctx = {
36959
+ params: parsed.params,
36960
+ query: parsed.query,
36961
+ body: parsed.body,
36962
+ headers: req.headers || {},
36963
+ method: req.method,
36964
+ path: req.path,
36965
+ route: req.route,
36966
+ auth: input.auth,
36967
+ app: input.app,
36968
+ get request() {
36969
+ return getRequest();
36970
+ },
36971
+ };
36972
+ result = await normalize(await handler(ctx));
36973
+ }
36974
+ }
36975
+ const duration = Date.now() - startTime;
36976
+ timing('complete', 'Web route completed', duration).catch((err) => console.error('Error logging timing', err));
36977
+ console.log('Web route', req.route || req.method + ' ' + req.path, 'answered', result.status, 'in', duration, 'ms');
36978
+ return result;
36979
+ } catch (error) {
36980
+ const duration = Date.now() - startTime;
36981
+ timing('error', 'Web route failed', duration, error).catch((err) => console.error('Error logging timing', err));
36982
+ console.error('Web route failed after', duration, 'ms:', error);
36983
+ throw error;
36984
+ }
36985
+ }`;
36986
+ function buildWebRouteWrapper(decompressedCode, input) {
36987
+ const code = `
36988
+ // Run the bundled module
36989
+ ${decompressedCode}
36990
+
36991
+ // Extract primitive from module exports
36992
+ const __exports = module.exports.default || module.exports;
36993
+ const __primitive = __exports?.primitive;
36994
+
36995
+ if (
36996
+ !__primitive ||
36997
+ (typeof __primitive.fetch !== 'function' && (!__primitive.routes || typeof __primitive.routes !== 'object'))
36998
+ ) {
36999
+ const __platformErr = new Error('[Lua] Invalid web-app artifact: missing primitive.routes or primitive.fetch');
37000
+ __platformErr.code = 'PLATFORM_VM_ERROR';
37001
+ throw __platformErr;
37002
+ }
37003
+
37004
+ // Wrapper function
37005
+ const wrapperFunction = ${WEB_ROUTE_WRAPPER_FUNCTION_SOURCE};
37006
+
37007
+ // Execute immediately and return result
37008
+ (async () => {
37009
+ return await wrapperFunction(${JSON.stringify(input)}, __primitive, typeof logTiming === 'function' ? logTiming : undefined, console);
37010
+ })();
37011
+ `;
37012
+ return {
37013
+ code,
37014
+ contextName: "WebRouteExecutionContext",
37015
+ contextOrigin: "web-route-execution",
37016
+ source: "web-route",
37017
+ timeoutMs: WEB_ROUTE_MAX_WALL_MS
37018
+ };
37019
+ }
37020
+ __name(buildWebRouteWrapper, "buildWebRouteWrapper");
37021
+ __name8(buildWebRouteWrapper, "buildWebRouteWrapper");
37022
+
36765
37023
  // src/utils/sandbox.ts
36766
37024
  init_dist4();
36767
37025
  import vm4 from "vm";
@@ -36882,8 +37140,8 @@ import path20 from "path";
36882
37140
  import { spawn as nodeSpawn } from "child_process";
36883
37141
  import { accessSync, constants as fsConstants, realpathSync, statSync as statSync5 } from "fs";
36884
37142
  import path19 from "path";
36885
- var __defProp8 = Object.defineProperty;
36886
- var __name8 = /* @__PURE__ */ __name((target, value3) => __defProp8(target, "name", { value: value3, configurable: true }), "__name");
37143
+ var __defProp9 = Object.defineProperty;
37144
+ var __name9 = /* @__PURE__ */ __name((target, value3) => __defProp9(target, "name", { value: value3, configurable: true }), "__name");
36887
37145
  var WORKFLOW_EXEC_BINARIES = Object.freeze([
36888
37146
  "git",
36889
37147
  "gh",
@@ -36910,7 +37168,7 @@ var WorkflowExecError = class extends Error {
36910
37168
  __name(this, "WorkflowExecError");
36911
37169
  }
36912
37170
  static {
36913
- __name8(this, "WorkflowExecError");
37171
+ __name9(this, "WorkflowExecError");
36914
37172
  }
36915
37173
  code;
36916
37174
  reason;
@@ -36944,7 +37202,7 @@ function isWorkflowExecError(err) {
36944
37202
  return !!e && typeof e === "object" && e.name === "ExecError" && typeof e.code === "string";
36945
37203
  }
36946
37204
  __name(isWorkflowExecError, "isWorkflowExecError");
36947
- __name8(isWorkflowExecError, "isWorkflowExecError");
37205
+ __name9(isWorkflowExecError, "isWorkflowExecError");
36948
37206
  function workflowExecRefusal(reason, detail, argv) {
36949
37207
  return new WorkflowExecError("EXEC_REFUSED", `[Lua] exec refused: ${detail} (EXEC_REFUSED/${reason})`, {
36950
37208
  reason,
@@ -36954,7 +37212,7 @@ function workflowExecRefusal(reason, detail, argv) {
36954
37212
  });
36955
37213
  }
36956
37214
  __name(workflowExecRefusal, "workflowExecRefusal");
36957
- __name8(workflowExecRefusal, "workflowExecRefusal");
37215
+ __name9(workflowExecRefusal, "workflowExecRefusal");
36958
37216
  var refused = workflowExecRefusal;
36959
37217
  var ALLOWLIST_TEXT = WORKFLOW_EXEC_BINARIES.join(", ");
36960
37218
  function parseCommandTemplate(template3, values) {
@@ -36965,13 +37223,13 @@ function parseCommandTemplate(template3, values) {
36965
37223
  const argv = [];
36966
37224
  let current = null;
36967
37225
  let quote = null;
36968
- const flush = /* @__PURE__ */ __name8(() => {
37226
+ const flush = /* @__PURE__ */ __name9(() => {
36969
37227
  if (current !== null) {
36970
37228
  argv.push(current);
36971
37229
  current = null;
36972
37230
  }
36973
37231
  }, "flush");
36974
- const scalar = /* @__PURE__ */ __name8((v, at) => {
37232
+ const scalar = /* @__PURE__ */ __name9((v, at) => {
36975
37233
  if (typeof v === "string") return v;
36976
37234
  if (typeof v === "number" && Number.isFinite(v)) return String(v);
36977
37235
  if (typeof v === "boolean") return String(v);
@@ -37032,7 +37290,7 @@ function parseCommandTemplate(template3, values) {
37032
37290
  return argv;
37033
37291
  }
37034
37292
  __name(parseCommandTemplate, "parseCommandTemplate");
37035
- __name8(parseCommandTemplate, "parseCommandTemplate");
37293
+ __name9(parseCommandTemplate, "parseCommandTemplate");
37036
37294
  function validateWorkflowExecArgv(argv) {
37037
37295
  if (!Array.isArray(argv)) throw refused("argv_invalid", 'argv must be an array of strings \u2014 exec(["git", "status"])');
37038
37296
  if (argv.length === 0) throw refused("argv_empty", "the command is empty");
@@ -37054,7 +37312,7 @@ function validateWorkflowExecArgv(argv) {
37054
37312
  ];
37055
37313
  }
37056
37314
  __name(validateWorkflowExecArgv, "validateWorkflowExecArgv");
37057
- __name8(validateWorkflowExecArgv, "validateWorkflowExecArgv");
37315
+ __name9(validateWorkflowExecArgv, "validateWorkflowExecArgv");
37058
37316
  function resolveWorkflowExecBinary(name, pathDirs) {
37059
37317
  if (typeof name !== "string" || name.length === 0) throw refused("argv_invalid", "argv[0] must name a binary");
37060
37318
  if (name.includes("/") || name.includes("\\") || name.startsWith(".")) {
@@ -37083,12 +37341,12 @@ function resolveWorkflowExecBinary(name, pathDirs) {
37083
37341
  ]);
37084
37342
  }
37085
37343
  __name(resolveWorkflowExecBinary, "resolveWorkflowExecBinary");
37086
- __name8(resolveWorkflowExecBinary, "resolveWorkflowExecBinary");
37344
+ __name9(resolveWorkflowExecBinary, "resolveWorkflowExecBinary");
37087
37345
  function within(root, p) {
37088
37346
  return p === root || p.startsWith(root.endsWith(path19.sep) ? root : root + path19.sep);
37089
37347
  }
37090
37348
  __name(within, "within");
37091
- __name8(within, "within");
37349
+ __name9(within, "within");
37092
37350
  function confineWorkflowExecCwd(root, cwd) {
37093
37351
  if (typeof root !== "string" || root.length === 0) throw refused("workspace_unavailable", "the step has no workspace");
37094
37352
  let rootReal;
@@ -37122,7 +37380,7 @@ function confineWorkflowExecCwd(root, cwd) {
37122
37380
  return abs;
37123
37381
  }
37124
37382
  __name(confineWorkflowExecCwd, "confineWorkflowExecCwd");
37125
- __name8(confineWorkflowExecCwd, "confineWorkflowExecCwd");
37383
+ __name9(confineWorkflowExecCwd, "confineWorkflowExecCwd");
37126
37384
  var RESERVED_ENV_KEY = /^(LUA_|LD_|DYLD_|GIT_|GH_|GITHUB_)|^(PATH|HOME|NODE_OPTIONS|BASH_ENV|ENV|TZ)$|_PROXY$/i;
37127
37385
  function checkWorkflowExecEnv(env) {
37128
37386
  if (env === void 0) return {};
@@ -37140,7 +37398,7 @@ function checkWorkflowExecEnv(env) {
37140
37398
  return out;
37141
37399
  }
37142
37400
  __name(checkWorkflowExecEnv, "checkWorkflowExecEnv");
37143
- __name8(checkWorkflowExecEnv, "checkWorkflowExecEnv");
37401
+ __name9(checkWorkflowExecEnv, "checkWorkflowExecEnv");
37144
37402
  var WORKFLOW_EXEC_GIT_DENIED_SUBCOMMANDS = Object.freeze([
37145
37403
  "push-mirror",
37146
37404
  "daemon",
@@ -37170,7 +37428,7 @@ function gitOptionMatches(arg, opt) {
37170
37428
  return opt.length === 2 && arg.length > 2 && arg.startsWith(opt);
37171
37429
  }
37172
37430
  __name(gitOptionMatches, "gitOptionMatches");
37173
- __name8(gitOptionMatches, "gitOptionMatches");
37431
+ __name9(gitOptionMatches, "gitOptionMatches");
37174
37432
  var GIT_PUSH_FORBIDDEN_FLAG = /^(--mirror|--delete|-d|-f|--force|--force-with-lease(=.*)?|--force-if-includes|--prune|--all|--tags)$/;
37175
37433
  function checkWorkflowExecGitArgv(argv, opts = {}) {
37176
37434
  if (argv[0] !== "git") return {};
@@ -37213,9 +37471,9 @@ function checkWorkflowExecGitArgv(argv, opts = {}) {
37213
37471
  };
37214
37472
  }
37215
37473
  __name(checkWorkflowExecGitArgv, "checkWorkflowExecGitArgv");
37216
- __name8(checkWorkflowExecGitArgv, "checkWorkflowExecGitArgv");
37474
+ __name9(checkWorkflowExecGitArgv, "checkWorkflowExecGitArgv");
37217
37475
  function formatWorkflowExecArgv(argv, max = WORKFLOW_EXEC_LOG_ARGV_MAX) {
37218
- const q = /* @__PURE__ */ __name8((a) => {
37476
+ const q = /* @__PURE__ */ __name9((a) => {
37219
37477
  const s = String(a);
37220
37478
  return s.length > 0 && /^[A-Za-z0-9_@%+=:,./-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
37221
37479
  }, "q");
@@ -37223,7 +37481,7 @@ function formatWorkflowExecArgv(argv, max = WORKFLOW_EXEC_LOG_ARGV_MAX) {
37223
37481
  return line.length > max ? `${line.slice(0, max)}\u2026` : line;
37224
37482
  }
37225
37483
  __name(formatWorkflowExecArgv, "formatWorkflowExecArgv");
37226
- __name8(formatWorkflowExecArgv, "formatWorkflowExecArgv");
37484
+ __name9(formatWorkflowExecArgv, "formatWorkflowExecArgv");
37227
37485
  function spawnWorkflowExec(o) {
37228
37486
  const spawnFn = o.spawnImpl ?? nodeSpawn;
37229
37487
  const max = o.outputMaxBytes ?? WORKFLOW_EXEC_OUTPUT_MAX_BYTES;
@@ -37258,7 +37516,7 @@ function spawnWorkflowExec(o) {
37258
37516
  let settled = false;
37259
37517
  let killTimer;
37260
37518
  let wallTimer;
37261
- const collect2 = /* @__PURE__ */ __name8((stream) => {
37519
+ const collect2 = /* @__PURE__ */ __name9((stream) => {
37262
37520
  const chunks = [];
37263
37521
  let bytes = 0;
37264
37522
  stream?.on("data", (c) => {
@@ -37281,7 +37539,7 @@ function spawnWorkflowExec(o) {
37281
37539
  }, "collect");
37282
37540
  const stdout = collect2(child.stdout);
37283
37541
  const stderr = collect2(child.stderr);
37284
- const killTree = /* @__PURE__ */ __name8((sig) => {
37542
+ const killTree = /* @__PURE__ */ __name9((sig) => {
37285
37543
  const pid = child.pid;
37286
37544
  if (!pid) return;
37287
37545
  try {
@@ -37290,7 +37548,7 @@ function spawnWorkflowExec(o) {
37290
37548
  } catch {
37291
37549
  }
37292
37550
  }, "killTree");
37293
- const end = /* @__PURE__ */ __name8(() => {
37551
+ const end = /* @__PURE__ */ __name9(() => {
37294
37552
  if (settled || timedOut) return;
37295
37553
  timedOut = true;
37296
37554
  killTree("SIGTERM");
@@ -37301,7 +37559,7 @@ function spawnWorkflowExec(o) {
37301
37559
  }, grace);
37302
37560
  killTimer.unref?.();
37303
37561
  }, "end");
37304
- const cleanup = /* @__PURE__ */ __name8(() => {
37562
+ const cleanup = /* @__PURE__ */ __name9(() => {
37305
37563
  if (wallTimer) clearTimeout(wallTimer);
37306
37564
  if (killTimer) clearTimeout(killTimer);
37307
37565
  o.signal?.removeEventListener("abort", end);
@@ -37335,7 +37593,7 @@ function spawnWorkflowExec(o) {
37335
37593
  });
37336
37594
  }
37337
37595
  __name(spawnWorkflowExec, "spawnWorkflowExec");
37338
- __name8(spawnWorkflowExec, "spawnWorkflowExec");
37596
+ __name9(spawnWorkflowExec, "spawnWorkflowExec");
37339
37597
  async function runWorkflowExec(o) {
37340
37598
  const argv = validateWorkflowExecArgv(o.request.argv);
37341
37599
  const file = resolveWorkflowExecBinary(argv[0], o.pathDirs);
@@ -37381,7 +37639,7 @@ async function runWorkflowExec(o) {
37381
37639
  });
37382
37640
  }
37383
37641
  __name(runWorkflowExec, "runWorkflowExec");
37384
- __name8(runWorkflowExec, "runWorkflowExec");
37642
+ __name9(runWorkflowExec, "runWorkflowExec");
37385
37643
  function createWorkflowExecQueue() {
37386
37644
  let tail = Promise.resolve();
37387
37645
  return (task) => {
@@ -37391,7 +37649,7 @@ function createWorkflowExecQueue() {
37391
37649
  };
37392
37650
  }
37393
37651
  __name(createWorkflowExecQueue, "createWorkflowExecQueue");
37394
- __name8(createWorkflowExecQueue, "createWorkflowExecQueue");
37652
+ __name9(createWorkflowExecQueue, "createWorkflowExecQueue");
37395
37653
  function stderrTail(s) {
37396
37654
  const t = s.trim();
37397
37655
  if (!t) return "";
@@ -37399,9 +37657,9 @@ function stderrTail(s) {
37399
37657
  return `: ${tail.replace(/\s+/g, " ")}`;
37400
37658
  }
37401
37659
  __name(stderrTail, "stderrTail");
37402
- __name8(stderrTail, "stderrTail");
37660
+ __name9(stderrTail, "stderrTail");
37403
37661
  function buildWorkflowExecApi(run) {
37404
- const call = /* @__PURE__ */ __name8(async (req, strict) => {
37662
+ const call = /* @__PURE__ */ __name9(async (req, strict) => {
37405
37663
  const out = await run(req);
37406
37664
  if (out.timedOut) {
37407
37665
  throw new WorkflowExecError("EXEC_TIMEOUT", `[Lua] ${formatWorkflowExecArgv(out.argv)} did not finish within its wall (${out.durationMs} ms) and was killed (EXEC_TIMEOUT)`, out);
@@ -37411,7 +37669,7 @@ function buildWorkflowExecApi(run) {
37411
37669
  }
37412
37670
  return out;
37413
37671
  }, "call");
37414
- const request2 = /* @__PURE__ */ __name8((argv, opts) => {
37672
+ const request2 = /* @__PURE__ */ __name9((argv, opts) => {
37415
37673
  const o = opts && typeof opts === "object" ? opts : {};
37416
37674
  return {
37417
37675
  argv,
@@ -37420,9 +37678,9 @@ function buildWorkflowExecApi(run) {
37420
37678
  env: o.env
37421
37679
  };
37422
37680
  }, "request");
37423
- const exec = /* @__PURE__ */ __name8((argv, opts) => call(request2(argv, opts), false), "exec");
37681
+ const exec = /* @__PURE__ */ __name9((argv, opts) => call(request2(argv, opts), false), "exec");
37424
37682
  exec.strict = (argv, opts) => call(request2(argv, opts), true);
37425
- const $ = /* @__PURE__ */ __name8(async (strings, ...values) => call({
37683
+ const $ = /* @__PURE__ */ __name9(async (strings, ...values) => call({
37426
37684
  argv: parseCommandTemplate(strings, values)
37427
37685
  }, false), "$");
37428
37686
  $.strict = async (strings, ...values) => call({
@@ -37434,16 +37692,16 @@ function buildWorkflowExecApi(run) {
37434
37692
  };
37435
37693
  }
37436
37694
  __name(buildWorkflowExecApi, "buildWorkflowExecApi");
37437
- __name8(buildWorkflowExecApi, "buildWorkflowExecApi");
37695
+ __name9(buildWorkflowExecApi, "buildWorkflowExecApi");
37438
37696
  function unavailableWorkflowExecApi(why) {
37439
- const reject = /* @__PURE__ */ __name8(async () => {
37697
+ const reject = /* @__PURE__ */ __name9(async () => {
37440
37698
  throw new WorkflowExecError("EXEC_UNAVAILABLE", `[Lua] ctx.exec is not available here: ${why} (EXEC_UNAVAILABLE)`);
37441
37699
  }, "reject");
37442
37700
  const exec = Object.assign(() => reject(), {
37443
- strict: /* @__PURE__ */ __name8(() => reject(), "strict")
37701
+ strict: /* @__PURE__ */ __name9(() => reject(), "strict")
37444
37702
  });
37445
37703
  const $ = Object.assign(() => reject(), {
37446
- strict: /* @__PURE__ */ __name8(() => reject(), "strict")
37704
+ strict: /* @__PURE__ */ __name9(() => reject(), "strict")
37447
37705
  });
37448
37706
  return {
37449
37707
  exec,
@@ -37451,7 +37709,7 @@ function unavailableWorkflowExecApi(why) {
37451
37709
  };
37452
37710
  }
37453
37711
  __name(unavailableWorkflowExecApi, "unavailableWorkflowExecApi");
37454
- __name8(unavailableWorkflowExecApi, "unavailableWorkflowExecApi");
37712
+ __name9(unavailableWorkflowExecApi, "unavailableWorkflowExecApi");
37455
37713
 
37456
37714
  // src/utils/workflow-exec.ts
37457
37715
  var WORKFLOW_LOCAL_EXEC_ENV_KEYS = [
@@ -39441,6 +39699,28 @@ module.exports = async (event) => {
39441
39699
  }
39442
39700
  }
39443
39701
  __name(executeWebhook, "executeWebhook");
39702
+ async function executeWebRoute(options) {
39703
+ const context = vm4.createContext(createSandbox(options));
39704
+ applyGlobalSelfReference(context);
39705
+ const wrapper = buildWebRouteWrapper(options.appCode, options.input);
39706
+ const pending = vm4.runInContext(wrapper.code, context, {
39707
+ filename: "web-app.bundle.js",
39708
+ timeout: wrapper.timeoutMs
39709
+ });
39710
+ let timer;
39711
+ const wall = new Promise((_, reject) => {
39712
+ timer = setTimeout(() => reject(new Error(`The route did not answer within ${wrapper.timeoutMs} ms (the wall-clock cap)`)), wrapper.timeoutMs);
39713
+ });
39714
+ try {
39715
+ return await Promise.race([
39716
+ pending,
39717
+ wall
39718
+ ]);
39719
+ } finally {
39720
+ clearTimeout(timer);
39721
+ }
39722
+ }
39723
+ __name(executeWebRoute, "executeWebRoute");
39444
39724
  async function executeJob(options) {
39445
39725
  const { jobCode, jobData, apiKey, agentId } = options;
39446
39726
  const sandbox = createSandbox(options);
@@ -39718,12 +39998,12 @@ function isWrittenAsModelClassRef(model) {
39718
39998
  }
39719
39999
  __name(isWrittenAsModelClassRef, "isWrittenAsModelClassRef");
39720
40000
  var oneOf = /* @__PURE__ */ __name((tuple) => tuple.map((v) => `\`${v}\``).join(", "), "oneOf");
39721
- function validateAgentNodeModelMembers(node, path27) {
40001
+ function validateAgentNodeModelMembers(node, path30) {
39722
40002
  const issues = [];
39723
40003
  if (typeof node.model === "string" && isWrittenAsModelClassRef(node.model)) {
39724
40004
  if (!modelClassOf(node.model)) {
39725
40005
  issues.push({
39726
- path: `${path27}.model`,
40006
+ path: `${path30}.model`,
39727
40007
  code: "model-class-unknown",
39728
40008
  message: `"${node.model}" is not a model class \u2014 use one of ${oneOf(WORKFLOW_MODEL_CLASSES.map((c) => modelClassRef(c)))}, or pin an approved model code`
39729
40009
  });
@@ -39732,7 +40012,7 @@ function validateAgentNodeModelMembers(node, path27) {
39732
40012
  if (node.taskClass !== void 0) {
39733
40013
  if (typeof node.taskClass !== "string" || !isWorkflowTaskClass(node.taskClass)) {
39734
40014
  issues.push({
39735
- path: `${path27}.taskClass`,
40015
+ path: `${path30}.taskClass`,
39736
40016
  code: "task-class-unknown",
39737
40017
  message: `"${String(node.taskClass)}" is not a task class \u2014 use one of ${oneOf(WORKFLOW_TASK_CLASSES)}`
39738
40018
  });
@@ -39741,7 +40021,7 @@ function validateAgentNodeModelMembers(node, path27) {
39741
40021
  if (node.requires !== void 0) {
39742
40022
  if (!Array.isArray(node.requires)) {
39743
40023
  issues.push({
39744
- path: `${path27}.requires`,
40024
+ path: `${path30}.requires`,
39745
40025
  code: "requires-invalid",
39746
40026
  message: `\`requires\` must be an array of ${oneOf(WORKFLOW_MODEL_TRAITS)}`
39747
40027
  });
@@ -39749,7 +40029,7 @@ function validateAgentNodeModelMembers(node, path27) {
39749
40029
  node.requires.forEach((trait, i) => {
39750
40030
  if (typeof trait !== "string" || !isWorkflowModelTrait(trait)) {
39751
40031
  issues.push({
39752
- path: `${path27}.requires.${i}`,
40032
+ path: `${path30}.requires.${i}`,
39753
40033
  code: "model-trait-unknown",
39754
40034
  message: `"${String(trait)}" is not a model trait \u2014 use one of ${oneOf(WORKFLOW_MODEL_TRAITS)}`
39755
40035
  });
@@ -39760,7 +40040,7 @@ function validateAgentNodeModelMembers(node, path27) {
39760
40040
  if (node.effort !== void 0) {
39761
40041
  if (typeof node.effort !== "string" || !isWorkflowEffort(node.effort)) {
39762
40042
  issues.push({
39763
- path: `${path27}.effort`,
40043
+ path: `${path30}.effort`,
39764
40044
  code: "effort-unknown",
39765
40045
  message: `"${String(node.effort)}" is not a workflow effort \u2014 use one of ${oneOf(WORKFLOW_AGENT_EFFORTS)}. (\`off\`, \`minimal\` and \`max\` are the agent-level \`modelSettings.reasoning.effort\` scale, not this one.)`
39766
40046
  });
@@ -39769,13 +40049,13 @@ function validateAgentNodeModelMembers(node, path27) {
39769
40049
  if (node.modelReason !== void 0) {
39770
40050
  if (typeof node.modelReason !== "string") {
39771
40051
  issues.push({
39772
- path: `${path27}.modelReason`,
40052
+ path: `${path30}.modelReason`,
39773
40053
  code: "model-reason-invalid",
39774
40054
  message: "`modelReason` must be a string"
39775
40055
  });
39776
40056
  } else if (node.modelReason.length > WORKFLOW_MODEL_REASON_MAX_LENGTH) {
39777
40057
  issues.push({
39778
- path: `${path27}.modelReason`,
40058
+ path: `${path30}.modelReason`,
39779
40059
  code: "model-reason-too-long",
39780
40060
  message: `\`modelReason\` is ${node.modelReason.length} characters \u2014 the cap is ${WORKFLOW_MODEL_REASON_MAX_LENGTH}`
39781
40061
  });
@@ -39795,22 +40075,22 @@ var MEMBERS = [
39795
40075
  function validateGraphModelMembers(graph) {
39796
40076
  const issues = [];
39797
40077
  const seen = /* @__PURE__ */ new Set();
39798
- const walk3 = /* @__PURE__ */ __name((value3, path27, depth) => {
40078
+ const walk4 = /* @__PURE__ */ __name((value3, path30, depth) => {
39799
40079
  if (depth > WORKFLOW_MODEL_WALK_MAX_DEPTH || value3 === null || typeof value3 !== "object") return;
39800
40080
  if (seen.has(value3)) return;
39801
40081
  seen.add(value3);
39802
40082
  if (Array.isArray(value3)) {
39803
- value3.forEach((v, i) => walk3(v, `${path27}.${i}`, depth + 1));
40083
+ value3.forEach((v, i) => walk4(v, `${path30}.${i}`, depth + 1));
39804
40084
  return;
39805
40085
  }
39806
40086
  const node = value3;
39807
40087
  if (MEMBERS.some((m) => node[m] !== void 0)) {
39808
- const label2 = typeof node.id === "string" && node.id ? node.id : path27;
40088
+ const label2 = typeof node.id === "string" && node.id ? node.id : path30;
39809
40089
  issues.push(...validateAgentNodeModelMembers(node, label2));
39810
40090
  }
39811
- for (const [k, v] of Object.entries(node)) walk3(v, path27 ? `${path27}.${k}` : k, depth + 1);
40091
+ for (const [k, v] of Object.entries(node)) walk4(v, path30 ? `${path30}.${k}` : k, depth + 1);
39812
40092
  }, "walk");
39813
- walk3(graph, "", 0);
40093
+ walk4(graph, "", 0);
39814
40094
  return issues;
39815
40095
  }
39816
40096
  __name(validateGraphModelMembers, "validateGraphModelMembers");
@@ -40064,6 +40344,325 @@ function workflowModelMemberError(workflowName, issues) {
40064
40344
  }
40065
40345
  __name(workflowModelMemberError, "workflowModelMemberError");
40066
40346
 
40347
+ // src/primitives/web-app.handler.ts
40348
+ init_types();
40349
+ init_constants();
40350
+ import path24 from "path";
40351
+
40352
+ // src/api/web-app.api.service.ts
40353
+ init_http_client();
40354
+ var WebAppApi = class extends HttpClient {
40355
+ static {
40356
+ __name(this, "WebAppApi");
40357
+ }
40358
+ agentId;
40359
+ constructor(baseUrl, credential, agentId) {
40360
+ super(baseUrl, credential);
40361
+ this.agentId = agentId;
40362
+ }
40363
+ /** Every web app of the agent, each with its versions (newest first). */
40364
+ async getWebApps() {
40365
+ return this.httpGet(`/developer/web-apps/${this.agentId}`, {});
40366
+ }
40367
+ /** Register the app; the server assigns its slug (the app origin's host label). */
40368
+ async createWebApp(data) {
40369
+ return this.httpPost(`/developer/web-apps/${this.agentId}`, data, {});
40370
+ }
40371
+ /** A new, inactive version; it goes live with the agent version that pins it. */
40372
+ async pushWebAppVersion(webAppId, data) {
40373
+ return this.httpPost(`/developer/web-apps/${this.agentId}/${webAppId}/version`, data, {});
40374
+ }
40375
+ };
40376
+
40377
+ // src/primitives/web-app.handler.ts
40378
+ init_artifact_loader();
40379
+
40380
+ // src/utils/web-app-build.ts
40381
+ init_dist();
40382
+ import crypto5 from "crypto";
40383
+ import fs17 from "fs";
40384
+ import os from "os";
40385
+ import path23 from "path";
40386
+ import { createRequire as createRequire2 } from "module";
40387
+ import { pathToFileURL as pathToFileURL2 } from "url";
40388
+ var WebAppBuildError = class extends Error {
40389
+ static {
40390
+ __name(this, "WebAppBuildError");
40391
+ }
40392
+ constructor(message) {
40393
+ super(message);
40394
+ this.name = "WebAppBuildError";
40395
+ }
40396
+ };
40397
+ var CONTENT_TYPES = {
40398
+ ".js": "text/javascript",
40399
+ ".mjs": "text/javascript",
40400
+ ".css": "text/css",
40401
+ ".html": "text/html; charset=utf-8",
40402
+ ".json": "application/json",
40403
+ ".map": "application/json",
40404
+ ".svg": "image/svg+xml",
40405
+ ".png": "image/png",
40406
+ ".jpg": "image/jpeg",
40407
+ ".jpeg": "image/jpeg",
40408
+ ".gif": "image/gif",
40409
+ ".webp": "image/webp",
40410
+ ".avif": "image/avif",
40411
+ ".ico": "image/x-icon",
40412
+ ".woff": "font/woff",
40413
+ ".woff2": "font/woff2",
40414
+ ".ttf": "font/ttf",
40415
+ ".otf": "font/otf",
40416
+ ".wasm": "application/wasm",
40417
+ ".txt": "text/plain; charset=utf-8",
40418
+ ".mp4": "video/mp4",
40419
+ ".webm": "video/webm",
40420
+ ".mp3": "audio/mpeg"
40421
+ };
40422
+ function contentTypeFor(file) {
40423
+ return CONTENT_TYPES[path23.extname(file).toLowerCase()] ?? "application/octet-stream";
40424
+ }
40425
+ __name(contentTypeFor, "contentTypeFor");
40426
+ async function loadAppVite(pagesDir) {
40427
+ const requireFromApp = createRequire2(path23.join(pagesDir, "package.json"));
40428
+ let entry;
40429
+ let version;
40430
+ try {
40431
+ entry = requireFromApp.resolve("vite");
40432
+ version = requireFromApp("vite/package.json").version;
40433
+ } catch {
40434
+ throw new WebAppBuildError(`No Vite installation found for ${pagesDir}. Install the page dependencies first (npm install in that directory).`);
40435
+ }
40436
+ const mod = await import(pathToFileURL2(entry).href);
40437
+ const build2 = mod.build ?? mod.default?.build;
40438
+ if (typeof build2 !== "function") throw new WebAppBuildError(`The Vite at ${entry} exposes no build() function.`);
40439
+ const createServer = mod.createServer ?? mod.default?.createServer;
40440
+ return {
40441
+ build: build2,
40442
+ createServer: typeof createServer === "function" ? createServer : void 0,
40443
+ version
40444
+ };
40445
+ }
40446
+ __name(loadAppVite, "loadAppVite");
40447
+ function walk3(dir, base = dir) {
40448
+ const out = [];
40449
+ for (const entry of fs17.readdirSync(dir, {
40450
+ withFileTypes: true
40451
+ })) {
40452
+ const full = path23.join(dir, entry.name);
40453
+ if (entry.isDirectory()) out.push(...walk3(full, base));
40454
+ else if (entry.isFile()) out.push(path23.relative(base, full).split(path23.sep).join("/"));
40455
+ }
40456
+ return out.sort();
40457
+ }
40458
+ __name(walk3, "walk");
40459
+ function collectBuildOutput(outDir) {
40460
+ const files = walk3(outDir);
40461
+ if (!files.includes("index.html")) {
40462
+ throw new WebAppBuildError(`The page build produced no index.html in ${outDir}.`);
40463
+ }
40464
+ const entryHtml = fs17.readFileSync(path23.join(outDir, "index.html"), "utf8");
40465
+ if (entryHtml.length > WEB_APP_ENTRY_HTML_MAX_CHARS) {
40466
+ throw new WebAppBuildError(`index.html is ${entryHtml.length} characters; the limit is ${WEB_APP_ENTRY_HTML_MAX_CHARS}. Move inline content into assets.`);
40467
+ }
40468
+ const assets = [];
40469
+ const skipped = [];
40470
+ for (const rel of files) {
40471
+ if (rel === "index.html") continue;
40472
+ if (!rel.startsWith("assets/")) {
40473
+ skipped.push(rel);
40474
+ continue;
40475
+ }
40476
+ if (rel.length > WEB_APP_ASSET_PATH_MAX_CHARS || !WEB_APP_ASSET_PATH_PATTERN.test(rel)) {
40477
+ throw new WebAppBuildError(`${rel} is not a servable asset path (letters, digits, . _ - / only, at most ${WEB_APP_ASSET_PATH_MAX_CHARS} characters). Rename the source file.`);
40478
+ }
40479
+ const bytes = fs17.readFileSync(path23.join(outDir, rel));
40480
+ if (bytes.length > WEB_APP_MAX_ASSET_BYTES) {
40481
+ throw new WebAppBuildError(`${rel} is ${bytes.length} bytes; a page asset may be at most ${WEB_APP_MAX_ASSET_BYTES} bytes.`);
40482
+ }
40483
+ assets.push({
40484
+ path: rel,
40485
+ hash: crypto5.createHash("sha256").update(bytes).digest("hex"),
40486
+ size: bytes.length,
40487
+ contentType: contentTypeFor(rel),
40488
+ bytes
40489
+ });
40490
+ }
40491
+ if (assets.length > WEB_APP_MAX_ASSETS) {
40492
+ throw new WebAppBuildError(`The page build produced ${assets.length} assets; the limit is ${WEB_APP_MAX_ASSETS}.`);
40493
+ }
40494
+ return {
40495
+ entryHtml,
40496
+ assets,
40497
+ skipped
40498
+ };
40499
+ }
40500
+ __name(collectBuildOutput, "collectBuildOutput");
40501
+ async function buildWebAppPages(pagesDir, loadVite = loadAppVite) {
40502
+ if (!fs17.existsSync(path23.join(pagesDir, "index.html"))) {
40503
+ throw new WebAppBuildError(`${pagesDir} has no index.html: \`pages.root\` must name the Vite project directory.`);
40504
+ }
40505
+ const { build: build2, version } = await loadVite(pagesDir);
40506
+ const outDir = fs17.mkdtempSync(path23.join(os.tmpdir(), "lua-web-app-"));
40507
+ try {
40508
+ await build2({
40509
+ root: pagesDir,
40510
+ base: "/",
40511
+ logLevel: "warn",
40512
+ html: {
40513
+ cspNonce: WEB_APP_CSP_NONCE_PLACEHOLDER
40514
+ },
40515
+ build: {
40516
+ outDir,
40517
+ emptyOutDir: true,
40518
+ assetsDir: "assets",
40519
+ manifest: false
40520
+ }
40521
+ });
40522
+ return {
40523
+ ...collectBuildOutput(outDir),
40524
+ viteVersion: version
40525
+ };
40526
+ } finally {
40527
+ fs17.rmSync(outDir, {
40528
+ recursive: true,
40529
+ force: true
40530
+ });
40531
+ }
40532
+ }
40533
+ __name(buildWebAppPages, "buildWebAppPages");
40534
+
40535
+ // src/primitives/web-app.handler.ts
40536
+ init_package_root();
40537
+ init_cli();
40538
+ init_write_info();
40539
+ init_base_handler();
40540
+ var WEB_APP_GOES_LIVE_WITH_AGENT_VERSION = "A web app version goes live with the agent version that pins it: create an agent version and promote it.";
40541
+ var WebAppHandler = class extends BaseVersionedHandler {
40542
+ static {
40543
+ __name(this, "WebAppHandler");
40544
+ }
40545
+ kind = PrimitiveKind.WEB_APP;
40546
+ displayName = "webapp";
40547
+ displayNamePlural = "web apps";
40548
+ /** No CLI delete in the MVP; unused because server-only apps are never stubbed (see shouldConsiderForOrphan). */
40549
+ deleteCommand = "";
40550
+ yamlConfig = {
40551
+ yamlKey: "webApps",
40552
+ idField: "webAppId"
40553
+ };
40554
+ getApi(apiKey, agentId) {
40555
+ return new WebAppApi(BASE_URLS.API, apiKey, agentId);
40556
+ }
40557
+ async fetchFromServer(api) {
40558
+ const response = await api.getWebApps();
40559
+ if (!response.success || !response.data?.webApps) return null;
40560
+ return response.data.webApps;
40561
+ }
40562
+ async createOnServer(api, primitive) {
40563
+ const response = await api.createWebApp({
40564
+ name: primitive.name,
40565
+ description: primitive.description || void 0
40566
+ });
40567
+ if (!response.success || !response.data?.id) return null;
40568
+ return response.data.id;
40569
+ }
40570
+ getActiveVersion(serverItem) {
40571
+ if (serverItem.activeVersionId) {
40572
+ return serverItem.versions?.find((v) => v.id === serverItem.activeVersionId)?.version ?? null;
40573
+ }
40574
+ return serverItem.versions?.find((v) => v.active === true)?.version ?? null;
40575
+ }
40576
+ /** The CLI cannot delete a web app (MVP), so an app only on the server is left alone, not stubbed. */
40577
+ shouldConsiderForOrphan(_item) {
40578
+ return false;
40579
+ }
40580
+ /**
40581
+ * Silent for an agent that has no web apps on either side, so every other
40582
+ * push and compile prints exactly what it printed before this kind existed.
40583
+ */
40584
+ async applySyncToYaml(serverData, config, manifest, apiCredentials) {
40585
+ const local = this.getFromYaml(config).length > 0;
40586
+ const compiled = !!manifest?.primitives.some((p) => p.kind === this.kind);
40587
+ const remote = (serverData.serverItems?.length ?? 0) > 0;
40588
+ if (!local && !compiled && !remote) return {
40589
+ messages: [],
40590
+ yamlUpdated: false,
40591
+ orphanedCount: 0
40592
+ };
40593
+ return super.applySyncToYaml(serverData, config, manifest, apiCredentials);
40594
+ }
40595
+ buildPushData(primitive, compressedCode, codeS3Hash) {
40596
+ const app = primitive;
40597
+ return {
40598
+ name: app.name,
40599
+ ...app.description ? {
40600
+ description: app.description
40601
+ } : {},
40602
+ ...codeS3Hash ? {
40603
+ codeS3Hash
40604
+ } : {
40605
+ code: compressedCode
40606
+ },
40607
+ routes: app.routes,
40608
+ hasFetch: app.hasFetch,
40609
+ nav: app.nav,
40610
+ ...app.csp ? {
40611
+ csp: app.csp
40612
+ } : {},
40613
+ ...app.branding ? {
40614
+ branding: app.branding
40615
+ } : {},
40616
+ compiler: {
40617
+ luaCli: getCliVersion()
40618
+ }
40619
+ };
40620
+ }
40621
+ /**
40622
+ * The page build: runs the app's Vite, adds every asset to the blob upload
40623
+ * (`bundleAccumulator`, keyed by the sha256 of the raw bytes) and the entry
40624
+ * HTML plus the asset manifest to the version.
40625
+ */
40626
+ async completePushData(pushData, manifest, name, bundleAccumulator) {
40627
+ const app = findPrimitive(manifest, name, this.kind);
40628
+ if (!app) throw new Error(`Web app "${name}" not found in compiled output.`);
40629
+ const pagesDir = path24.resolve(path24.dirname(app.sourcePath), app.pagesRoot);
40630
+ writeProgress(`\u{1F9F1} Building the pages of ${name} (${path24.relative(process.cwd(), pagesDir) || "."})...`);
40631
+ const pages = await buildWebAppPages(pagesDir);
40632
+ for (const asset of pages.assets) bundleAccumulator.set(asset.hash, asset.bytes);
40633
+ const bytes = pages.assets.reduce((n2, a) => n2 + a.size, 0);
40634
+ writeSuccess(`\u2705 Pages built: ${pages.assets.length} asset(s), ${Math.round(bytes / 1024)} KiB (Vite ${pages.viteVersion})`);
40635
+ if (pages.skipped.length > 0) {
40636
+ writeInfo(`\u26A0\uFE0F Not served (outside assets/): ${pages.skipped.slice(0, 10).join(", ")}${pages.skipped.length > 10 ? ", \u2026" : ""}. Import files from your source so Vite emits them under assets/.`);
40637
+ }
40638
+ const compiler = pushData.compiler ?? {};
40639
+ return {
40640
+ ...pushData,
40641
+ entryHtml: pages.entryHtml,
40642
+ assets: pages.assets.map(({ bytes: _bytes, ...entry }) => entry),
40643
+ compiler: {
40644
+ ...compiler,
40645
+ vite: pages.viteVersion
40646
+ }
40647
+ };
40648
+ }
40649
+ async pushToServer(apiKey, agentId, entityId, pushData) {
40650
+ const api = this.getApi(apiKey, agentId);
40651
+ const response = await api.pushWebAppVersion(entityId, pushData);
40652
+ return {
40653
+ success: response.success,
40654
+ error: response.error?.message
40655
+ };
40656
+ }
40657
+ async publishVersion(_apiKey, _agentId, _entityId, _version) {
40658
+ return {
40659
+ success: false,
40660
+ error: WEB_APP_GOES_LIVE_WITH_AGENT_VERSION
40661
+ };
40662
+ }
40663
+ };
40664
+ var webAppHandler = new WebAppHandler();
40665
+
40067
40666
  // src/primitives/index.ts
40068
40667
  init_skill_handler();
40069
40668
  init_mcp_server_handler();
@@ -40081,7 +40680,9 @@ var primitiveHandlers = {
40081
40680
  [PrimitiveKind.DEVICE_TRIGGER]: deviceTriggerHandler,
40082
40681
  [PrimitiveKind.VOICE]: voiceHandler,
40083
40682
  // WB-09 (WF-543): dark — no `workflows:` rows in lua.skill.yaml ⇒ never selected.
40084
- [PrimitiveKind.WORKFLOW]: workflowHandler
40683
+ [PrimitiveKind.WORKFLOW]: workflowHandler,
40684
+ // Lua Apps: inert (and silent, see WebAppHandler.applySyncToYaml) for an agent without `webApps`.
40685
+ [PrimitiveKind.WEB_APP]: webAppHandler
40085
40686
  };
40086
40687
  var versionedHandlers = Object.values(primitiveHandlers).filter((h) => isVersionedHandler(h));
40087
40688
  var syncableHandlers = Object.values(primitiveHandlers);
@@ -40122,7 +40723,7 @@ async function compileCommand(options) {
40122
40723
  }
40123
40724
  writeProgress("\u{1F528} Compiling...");
40124
40725
  const rootDir = process.cwd();
40125
- const outDir = path23.join(rootDir, "dist-v2");
40726
+ const outDir = path25.join(rootDir, "dist-v2");
40126
40727
  const result = await compile2({
40127
40728
  rootDir,
40128
40729
  outDir,
@@ -40293,6 +40894,415 @@ __name(printToolOutput, "printToolOutput");
40293
40894
  // src/commands/test.ts
40294
40895
  init_cli();
40295
40896
  init_command_utils();
40897
+
40898
+ // src/utils/web-app-route.ts
40899
+ init_dist();
40900
+ var WebRouteSpecError = class extends Error {
40901
+ static {
40902
+ __name(this, "WebRouteSpecError");
40903
+ }
40904
+ constructor(message) {
40905
+ super(message);
40906
+ this.name = "WebRouteSpecError";
40907
+ }
40908
+ };
40909
+ function parseRouteSpec(spec) {
40910
+ const match = /^\s*([A-Za-z]+)\s+(\/\S*)\s*$/.exec(spec);
40911
+ const method = match?.[1].toUpperCase();
40912
+ if (!match || !WEB_ROUTE_METHODS.includes(method)) {
40913
+ throw new WebRouteSpecError(`--route must look like 'GET /path?query' (${WEB_ROUTE_METHODS.join(", ")}); got ${JSON.stringify(spec)}`);
40914
+ }
40915
+ const url = new URL(match[2], "http://local");
40916
+ const query = {};
40917
+ for (const [key, value3] of url.searchParams) {
40918
+ const existing = query[key];
40919
+ if (existing === void 0) query[key] = value3;
40920
+ else if (Array.isArray(existing)) existing.push(value3);
40921
+ else query[key] = [
40922
+ existing,
40923
+ value3
40924
+ ];
40925
+ }
40926
+ return {
40927
+ method,
40928
+ path: url.pathname,
40929
+ query
40930
+ };
40931
+ }
40932
+ __name(parseRouteSpec, "parseRouteSpec");
40933
+ var segmentsOf = /* @__PURE__ */ __name((p) => p.split("/").filter(Boolean), "segmentsOf");
40934
+ function matchManifestRoute(routes, method, path30) {
40935
+ const parts = segmentsOf(path30);
40936
+ const candidates = routes.map((r, order) => ({
40937
+ r,
40938
+ order,
40939
+ segs: segmentsOf(r.path)
40940
+ })).filter(({ r, segs }) => r.method === method && segs.length === parts.length).sort((a, b) => b.segs.filter((s) => !s.startsWith(":")).length - a.segs.filter((s) => !s.startsWith(":")).length || a.order - b.order);
40941
+ for (const { r, segs } of candidates) {
40942
+ const params = {};
40943
+ let ok = true;
40944
+ for (let i = 0; i < segs.length && ok; i++) {
40945
+ if (segs[i].startsWith(":")) {
40946
+ try {
40947
+ params[segs[i].slice(1)] = decodeURIComponent(parts[i]);
40948
+ } catch {
40949
+ ok = false;
40950
+ }
40951
+ } else if (segs[i] !== parts[i]) {
40952
+ ok = false;
40953
+ }
40954
+ }
40955
+ if (ok) return {
40956
+ key: r.key,
40957
+ params
40958
+ };
40959
+ }
40960
+ return null;
40961
+ }
40962
+ __name(matchManifestRoute, "matchManifestRoute");
40963
+ function buildWebRouteInput(opts) {
40964
+ const headers = {};
40965
+ for (const [name, value3] of Object.entries(opts.headers ?? {})) {
40966
+ if (typeof value3 === "string" && isForwardableWebRouteRequestHeader(name)) headers[name.toLowerCase()] = value3;
40967
+ }
40968
+ let body;
40969
+ if (opts.body !== void 0 && opts.spec.method !== "GET") {
40970
+ body = typeof opts.body === "string" ? {
40971
+ kind: "text",
40972
+ value: opts.body
40973
+ } : {
40974
+ kind: "json",
40975
+ value: opts.body
40976
+ };
40977
+ headers["content-type"] ??= body.kind === "json" ? "application/json" : "text/plain";
40978
+ }
40979
+ return {
40980
+ v: WEB_ROUTE_PROTOCOL_VERSION,
40981
+ request: {
40982
+ method: opts.spec.method,
40983
+ path: opts.spec.path,
40984
+ ...opts.route ? {
40985
+ route: opts.route.key,
40986
+ params: opts.route.params
40987
+ } : {
40988
+ params: {}
40989
+ },
40990
+ query: opts.spec.query,
40991
+ headers,
40992
+ ...body ? {
40993
+ body
40994
+ } : {}
40995
+ },
40996
+ auth: opts.auth,
40997
+ app: {
40998
+ id: opts.app.id,
40999
+ name: opts.app.name,
41000
+ version: opts.app.version,
41001
+ deployment: 0,
41002
+ origin: "http://localhost",
41003
+ preview: false
41004
+ }
41005
+ };
41006
+ }
41007
+ __name(buildWebRouteInput, "buildWebRouteInput");
41008
+
41009
+ // src/utils/web-app-dev.ts
41010
+ init_dist();
41011
+ init_credential_operational_context();
41012
+ init_cli_error();
41013
+ async function developerAuth(apiKey, agentId, config) {
41014
+ const credential = await resolveCredentialOperationalContext(apiKey);
41015
+ if (!credential.userId) {
41016
+ throw CliError.usage("A web app route runs as a signed-in person; this credential has no user. Run `lua auth configure` and sign in.");
41017
+ }
41018
+ const orgId = config?.agent?.orgId || credential.authorization.orgs[0]?.orgId;
41019
+ if (!orgId) {
41020
+ throw CliError.usage("No organization for this project: lua.skill.yaml has no agent.orgId and the credential belongs to no organization. Run `lua init` in the project.");
41021
+ }
41022
+ return {
41023
+ userId: credential.userId,
41024
+ orgId,
41025
+ agentId,
41026
+ subjectType: "user",
41027
+ credentialType: "firstPartySession",
41028
+ roles: [],
41029
+ scopes: [],
41030
+ ...credential.identity?.fullName ? {
41031
+ name: credential.identity.fullName
41032
+ } : {},
41033
+ ...credential.identity?.email ? {
41034
+ email: credential.identity.email
41035
+ } : {}
41036
+ };
41037
+ }
41038
+ __name(developerAuth, "developerAuth");
41039
+ function devBootstrap(app, origin) {
41040
+ return {
41041
+ v: 1,
41042
+ app: {
41043
+ id: "local",
41044
+ name: app.name,
41045
+ version: "0.0.0-dev",
41046
+ deployment: 0,
41047
+ origin
41048
+ },
41049
+ apiBase: "/_lua/api",
41050
+ manifest: "/_lua/manifest",
41051
+ auth: {
41052
+ origin: "",
41053
+ exchangePath: "",
41054
+ audience: "web-app"
41055
+ },
41056
+ embedded: false,
41057
+ hostOrigins: [],
41058
+ theme: "dark",
41059
+ density: "compact",
41060
+ preview: false,
41061
+ dev: true
41062
+ };
41063
+ }
41064
+ __name(devBootstrap, "devBootstrap");
41065
+ function devBootstrapScript(app, origin) {
41066
+ const json2 = JSON.stringify(devBootstrap(app, origin)).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
41067
+ return `<script>window.__LUA__=${json2};</script>`;
41068
+ }
41069
+ __name(devBootstrapScript, "devBootstrapScript");
41070
+ var json = /* @__PURE__ */ __name((status, value3) => ({
41071
+ status,
41072
+ headers: {
41073
+ "content-type": "application/json",
41074
+ "cache-control": "no-store"
41075
+ },
41076
+ body: Buffer.from(JSON.stringify(value3), "utf8")
41077
+ }), "json");
41078
+ function shapeBody(raw, contentType) {
41079
+ if (!raw || raw.length === 0) return void 0;
41080
+ const ct = (contentType || "").toLowerCase();
41081
+ if (ct.includes("json")) return {
41082
+ kind: "json",
41083
+ value: JSON.parse(raw.toString("utf8"))
41084
+ };
41085
+ if (ct.startsWith("text/") || ct.includes("xml") || ct.includes("x-www-form-urlencoded")) {
41086
+ return {
41087
+ kind: "text",
41088
+ value: raw.toString("utf8")
41089
+ };
41090
+ }
41091
+ return {
41092
+ kind: "base64",
41093
+ value: raw.toString("base64")
41094
+ };
41095
+ }
41096
+ __name(shapeBody, "shapeBody");
41097
+ function materialize(result) {
41098
+ const headers = {
41099
+ "cache-control": "no-store",
41100
+ ...result.headers
41101
+ };
41102
+ const body = result.body;
41103
+ if (!body) return {
41104
+ status: result.status,
41105
+ headers
41106
+ };
41107
+ if (body.kind === "json") {
41108
+ headers["content-type"] ??= "application/json";
41109
+ return {
41110
+ status: result.status,
41111
+ headers,
41112
+ body: Buffer.from(JSON.stringify(body.value ?? null), "utf8")
41113
+ };
41114
+ }
41115
+ if (body.kind === "text") {
41116
+ headers["content-type"] ??= "text/plain; charset=utf-8";
41117
+ return {
41118
+ status: result.status,
41119
+ headers,
41120
+ body: Buffer.from(body.value ?? "", "utf8")
41121
+ };
41122
+ }
41123
+ return {
41124
+ status: result.status,
41125
+ headers,
41126
+ body: Buffer.from(body.value ?? "", "base64")
41127
+ };
41128
+ }
41129
+ __name(materialize, "materialize");
41130
+ async function handleDevRequest(req, ctx) {
41131
+ const url = new URL(req.url, "http://local");
41132
+ if (url.pathname === "/_lua/manifest") {
41133
+ return json(200, {
41134
+ v: 1,
41135
+ app: {
41136
+ id: "local",
41137
+ name: ctx.app.name,
41138
+ description: ctx.app.description,
41139
+ version: "0.0.0-dev",
41140
+ deployment: 0
41141
+ },
41142
+ nav: ctx.app.nav,
41143
+ user: {
41144
+ id: ctx.auth.userId,
41145
+ name: ctx.auth.name,
41146
+ email: ctx.auth.email
41147
+ },
41148
+ org: {
41149
+ id: ctx.auth.orgId
41150
+ },
41151
+ agent: {
41152
+ id: ctx.auth.agentId
41153
+ },
41154
+ auth: {
41155
+ roles: ctx.auth.roles,
41156
+ scopes: ctx.auth.scopes
41157
+ }
41158
+ });
41159
+ }
41160
+ if (url.pathname !== "/_lua/api" && !url.pathname.startsWith("/_lua/api/")) return null;
41161
+ const method = req.method.toUpperCase();
41162
+ if (!WEB_ROUTE_METHODS.includes(method)) {
41163
+ return json(405, {
41164
+ code: "METHOD_NOT_ALLOWED",
41165
+ message: `Method ${method} is not supported`
41166
+ });
41167
+ }
41168
+ if (req.bodyTooLarge || req.body && req.body.length > WEB_ROUTE_MAX_REQUEST_BODY_BYTES) {
41169
+ return json(413, {
41170
+ code: "VALIDATION",
41171
+ message: `Request body exceeds ${WEB_ROUTE_MAX_REQUEST_BODY_BYTES} bytes`
41172
+ });
41173
+ }
41174
+ const routePath = url.pathname.slice("/_lua/api".length) || "/";
41175
+ const spec = parseRouteSpec(`${method} ${routePath}${url.search}`);
41176
+ const route = matchManifestRoute(ctx.app.routes, spec.method, spec.path);
41177
+ if (!route && !ctx.app.hasFetch) {
41178
+ return json(404, {
41179
+ code: "NOT_FOUND",
41180
+ message: `No route for ${spec.method} ${spec.path}`
41181
+ });
41182
+ }
41183
+ let body;
41184
+ try {
41185
+ const ct = req.headers["content-type"];
41186
+ body = shapeBody(req.body, Array.isArray(ct) ? ct[0] : ct);
41187
+ } catch {
41188
+ return json(400, {
41189
+ code: "VALIDATION",
41190
+ message: "Request body is not valid JSON"
41191
+ });
41192
+ }
41193
+ const headers = {};
41194
+ for (const [name, value3] of Object.entries(req.headers)) if (typeof value3 === "string") headers[name] = value3;
41195
+ const input = buildWebRouteInput({
41196
+ spec,
41197
+ route,
41198
+ headers,
41199
+ auth: ctx.auth,
41200
+ app: {
41201
+ id: "local",
41202
+ name: ctx.app.name,
41203
+ version: "0.0.0-dev"
41204
+ }
41205
+ });
41206
+ if (body && spec.method !== "GET") input.request.body = body;
41207
+ try {
41208
+ const execute = ctx.execute ?? executeWebRoute;
41209
+ return materialize(await execute({
41210
+ appCode: ctx.appCode(),
41211
+ input,
41212
+ apiKey: ctx.apiKey,
41213
+ agentId: ctx.agentId
41214
+ }));
41215
+ } catch (error) {
41216
+ console.error(error);
41217
+ return json(500, {
41218
+ code: "HANDLER_ERROR",
41219
+ message: error?.message || "The route handler failed"
41220
+ });
41221
+ }
41222
+ }
41223
+ __name(handleDevRequest, "handleDevRequest");
41224
+ function readBody(req) {
41225
+ return new Promise((resolve7, reject) => {
41226
+ const chunks = [];
41227
+ let total = 0;
41228
+ let settled = false;
41229
+ const onData = /* @__PURE__ */ __name((chunk) => {
41230
+ total += chunk.length;
41231
+ if (total > WEB_ROUTE_MAX_REQUEST_BODY_BYTES) {
41232
+ settled = true;
41233
+ req.off("data", onData);
41234
+ req.pause();
41235
+ resolve7({
41236
+ body: Buffer.alloc(0),
41237
+ tooLarge: true
41238
+ });
41239
+ return;
41240
+ }
41241
+ chunks.push(chunk);
41242
+ }, "onData");
41243
+ req.on("data", onData);
41244
+ req.on("end", () => settled || resolve7({
41245
+ body: Buffer.concat(chunks),
41246
+ tooLarge: false
41247
+ }));
41248
+ req.on("error", (error) => settled || reject(error));
41249
+ });
41250
+ }
41251
+ __name(readBody, "readBody");
41252
+ function isRouteSource(file, appDir, pagesDir) {
41253
+ const inApp = file.startsWith(appDir + "/") || file.startsWith(appDir + "\\");
41254
+ const inPages = file === pagesDir || file.startsWith(pagesDir + "/") || file.startsWith(pagesDir + "\\");
41255
+ return inApp && !inPages && /\.[cm]?[jt]sx?$/.test(file);
41256
+ }
41257
+ __name(isRouteSource, "isRouteSource");
41258
+ function luaAppsDevPlugin(opts) {
41259
+ return {
41260
+ name: "lua-apps-dev",
41261
+ configureServer(dev) {
41262
+ dev.middlewares.use(async (req, res, next) => {
41263
+ if (!String(req.url ?? "").startsWith("/_lua/")) return next();
41264
+ try {
41265
+ const read = await readBody(req);
41266
+ const answer = await handleDevRequest({
41267
+ method: req.method ?? "GET",
41268
+ url: req.url ?? "/",
41269
+ headers: req.headers,
41270
+ body: read.body,
41271
+ bodyTooLarge: read.tooLarge
41272
+ }, opts.context());
41273
+ if (!answer) return next();
41274
+ res.statusCode = answer.status;
41275
+ for (const [k, v] of Object.entries(answer.headers)) res.setHeader(k, v);
41276
+ res.end(answer.body);
41277
+ if (read.tooLarge) req.resume();
41278
+ opts.log?.(`${req.method} ${req.url} \u2192 ${answer.status}`, answer.status);
41279
+ } catch (error) {
41280
+ res.statusCode = 500;
41281
+ res.setHeader("content-type", "application/json");
41282
+ res.end(JSON.stringify({
41283
+ code: "PLATFORM_ERROR",
41284
+ message: String(error?.message ?? error)
41285
+ }));
41286
+ }
41287
+ });
41288
+ if (opts.onRouteChange) {
41289
+ dev.watcher.add(opts.appDir);
41290
+ dev.watcher.on("change", (file) => {
41291
+ if (isRouteSource(file, opts.appDir, opts.pagesDir)) void opts.onRouteChange(file);
41292
+ });
41293
+ }
41294
+ },
41295
+ transformIndexHtml(html) {
41296
+ const script = devBootstrapScript(opts.context().app, opts.origin() || "http://localhost");
41297
+ return html.includes("</head>") ? html.replace("</head>", `${script}
41298
+ </head>`) : `${script}
41299
+ ${html}`;
41300
+ }
41301
+ };
41302
+ }
41303
+ __name(luaAppsDevPlugin, "luaAppsDevPlugin");
41304
+
41305
+ // src/commands/test.ts
40296
41306
  init_files();
40297
41307
  init_artifact_loader();
40298
41308
  init_types();
@@ -40397,9 +41407,13 @@ var ALIAS_MAP = {
40397
41407
  "device-trigger",
40398
41408
  "voice",
40399
41409
  "workflow",
41410
+ "webapp",
40400
41411
  "backup"
40401
41412
  ],
40402
41413
  aliases: lowerKeys({
41414
+ webapps: "webapp",
41415
+ "web-app": "webapp",
41416
+ "web-apps": "webapp",
40403
41417
  skills: "skill",
40404
41418
  sk: "skill",
40405
41419
  voices: "voice",
@@ -40526,9 +41540,13 @@ var ALIAS_MAP = {
40526
41540
  "job",
40527
41541
  "preprocessor",
40528
41542
  "postprocessor",
40529
- "workflow"
41543
+ "workflow",
41544
+ "webapp"
40530
41545
  ],
40531
41546
  aliases: lowerKeys({
41547
+ webapps: "webapp",
41548
+ "web-app": "webapp",
41549
+ "web-apps": "webapp",
40532
41550
  workflows: "workflow",
40533
41551
  wf: "workflow",
40534
41552
  skills: "skill",
@@ -41236,8 +42254,8 @@ function validateOrSuggest(group, input, opts) {
41236
42254
  __name(validateOrSuggest, "validateOrSuggest");
41237
42255
 
41238
42256
  // src/commands/workflow-local-run.ts
41239
- import fs17 from "fs";
41240
- import path24 from "path";
42257
+ import fs18 from "fs";
42258
+ import path26 from "path";
41241
42259
  init_cli();
41242
42260
 
41243
42261
  // src/utils/stdout-redirect.ts
@@ -41455,13 +42473,13 @@ __name(runScriptWorkflowLocally, "runScriptWorkflowLocally");
41455
42473
 
41456
42474
  // src/utils/workflow-script-replay.ts
41457
42475
  init_dist4();
41458
- import { createRequire as createRequire2 } from "module";
42476
+ import { createRequire as createRequire3 } from "module";
41459
42477
  import vm5 from "vm";
41460
42478
  import { createHash as createHash7, randomUUID as randomUUID4 } from "crypto";
41461
42479
 
41462
42480
  // ../sandbox-runtime/dist/workflow-script-wrapper.mjs
41463
- var __defProp9 = Object.defineProperty;
41464
- var __name9 = /* @__PURE__ */ __name((target, value3) => __defProp9(target, "name", { value: value3, configurable: true }), "__name");
42481
+ var __defProp10 = Object.defineProperty;
42482
+ var __name10 = /* @__PURE__ */ __name((target, value3) => __defProp10(target, "name", { value: value3, configurable: true }), "__name");
41465
42483
  var WORKFLOW_SCRIPT_RUNTIME_KEY = "__luaWorkflowScriptRuntime";
41466
42484
  var WORKFLOW_SCRIPT_PROTOCOL_VERSION = 1;
41467
42485
  var WORKFLOW_SCRIPT_DEFAULT_CAPS = {
@@ -41796,18 +42814,18 @@ function computeWorkflowProtocolShipWindow(input) {
41796
42814
  };
41797
42815
  }
41798
42816
  __name(computeWorkflowProtocolShipWindow, "computeWorkflowProtocolShipWindow");
41799
- __name9(computeWorkflowProtocolShipWindow, "computeWorkflowProtocolShipWindow");
42817
+ __name10(computeWorkflowProtocolShipWindow, "computeWorkflowProtocolShipWindow");
41800
42818
  var WORKFLOW_SCRIPT_EFFORT_PROTOCOL_VERSION = 2;
41801
42819
  function scriptEffortForwardable(journalProtocolVersion) {
41802
42820
  return typeof journalProtocolVersion === "number" && journalProtocolVersion >= WORKFLOW_SCRIPT_EFFORT_PROTOCOL_VERSION;
41803
42821
  }
41804
42822
  __name(scriptEffortForwardable, "scriptEffortForwardable");
41805
- __name9(scriptEffortForwardable, "scriptEffortForwardable");
42823
+ __name10(scriptEffortForwardable, "scriptEffortForwardable");
41806
42824
  function workflowScriptReplaySource(journalProtocolVersion) {
41807
42825
  return WORKFLOW_SCRIPT_PROTOCOL_VERSIONS.includes(journalProtocolVersion) ? WORKFLOW_SCRIPT_REPLAY_SOURCES[journalProtocolVersion] : void 0;
41808
42826
  }
41809
42827
  __name(workflowScriptReplaySource, "workflowScriptReplaySource");
41810
- __name9(workflowScriptReplaySource, "workflowScriptReplaySource");
42828
+ __name10(workflowScriptReplaySource, "workflowScriptReplaySource");
41811
42829
  var HOST_BINDINGS = [
41812
42830
  "args",
41813
42831
  "agent",
@@ -41836,7 +42854,7 @@ function transformWorkflowScriptModule(script) {
41836
42854
  return script.replace(/^\s*export\s+const\s+meta\s*=/m, "const meta =");
41837
42855
  }
41838
42856
  __name(transformWorkflowScriptModule, "transformWorkflowScriptModule");
41839
- __name9(transformWorkflowScriptModule, "transformWorkflowScriptModule");
42857
+ __name10(transformWorkflowScriptModule, "transformWorkflowScriptModule");
41840
42858
  function buildWorkflowScriptWrapper(script, input) {
41841
42859
  const tick = input ?? {};
41842
42860
  const version = tick.journalProtocolVersion ?? WORKFLOW_SCRIPT_PROTOCOL_VERSION;
@@ -41886,7 +42904,7 @@ ${transformWorkflowScriptModule(script)}
41886
42904
  };
41887
42905
  }
41888
42906
  __name(buildWorkflowScriptWrapper, "buildWorkflowScriptWrapper");
41889
- __name9(buildWorkflowScriptWrapper, "buildWorkflowScriptWrapper");
42907
+ __name10(buildWorkflowScriptWrapper, "buildWorkflowScriptWrapper");
41890
42908
 
41891
42909
  // src/utils/workflow-script-replay.ts
41892
42910
  var ReplayRuntimeUnavailableError = class extends Error {
@@ -42010,7 +43028,7 @@ __name(bundledScriptRunner, "bundledScriptRunner");
42010
43028
  function projectScriptRunner(projectDir) {
42011
43029
  let runtime;
42012
43030
  try {
42013
- const req = createRequire2(`${projectDir.replace(/\/$/, "")}/package.json`);
43031
+ const req = createRequire3(`${projectDir.replace(/\/$/, "")}/package.json`);
42014
43032
  runtime = req("@lua/sandbox-runtime");
42015
43033
  } catch (e) {
42016
43034
  throw new ReplayRuntimeUnavailableError(e.message);
@@ -42320,7 +43338,7 @@ async function runWorkflowLocalFromProject(name, flags) {
42320
43338
  fastRetries: !!flags.fastRetries,
42321
43339
  realTime: !!flags.realTime,
42322
43340
  artefactsDir: flags.artefactsDir,
42323
- workspaceDir: flags.workspace ? path24.resolve(flags.workspace) : void 0,
43341
+ workspaceDir: flags.workspace ? path26.resolve(flags.workspace) : void 0,
42324
43342
  env: parseEnvFlags(flags.env),
42325
43343
  prompt: process.stdin.isTTY && !asJson && !isCiModeEnabled() ? promptForLocalDecision : void 0,
42326
43344
  log: asJson ? void 0 : (line) => console.log(line)
@@ -42471,10 +43489,10 @@ async function runScriptFormLocally(wf, flags, say) {
42471
43489
  }
42472
43490
  __name(runScriptFormLocally, "runScriptFormLocally");
42473
43491
  function writeLedgerFile(file, value3) {
42474
- fs17.mkdirSync(path24.dirname(path24.resolve(file)), {
43492
+ fs18.mkdirSync(path26.dirname(path26.resolve(file)), {
42475
43493
  recursive: true
42476
43494
  });
42477
- fs17.writeFileSync(file, `${JSON.stringify(value3, null, 2)}
43495
+ fs18.writeFileSync(file, `${JSON.stringify(value3, null, 2)}
42478
43496
  `);
42479
43497
  }
42480
43498
  __name(writeLedgerFile, "writeLedgerFile");
@@ -42668,6 +43686,10 @@ async function testCommand(type, cmdObj) {
42668
43686
  {
42669
43687
  name: "\u{1F4E4} PostProcessor",
42670
43688
  value: "postprocessor"
43689
+ },
43690
+ {
43691
+ name: "\u{1F5A5}\uFE0F Web app route",
43692
+ value: "webapp"
42671
43693
  }
42672
43694
  ]
42673
43695
  }
@@ -42685,6 +43707,9 @@ async function testCommand(type, cmdObj) {
42685
43707
  case "webhook":
42686
43708
  await testWebhook(entityName, inputJson, asJson);
42687
43709
  break;
43710
+ case "webapp":
43711
+ await testWebApp(entityName, cmdObj?.route ?? null, inputJson, asJson);
43712
+ break;
42688
43713
  case "job":
42689
43714
  await testJob(entityName, asJson);
42690
43715
  break;
@@ -42924,6 +43949,108 @@ async function testWebhook(entityName, inputJson, asJson = false) {
42924
43949
  });
42925
43950
  }
42926
43951
  __name(testWebhook, "testWebhook");
43952
+ async function testWebApp(entityName, routeSpec, inputJson, asJson = false) {
43953
+ progress(asJson, "\u{1F5A5}\uFE0F Testing web app route...");
43954
+ progress(asJson, "\u{1F4E6} Compiling code first...");
43955
+ await compileQuietly(asJson);
43956
+ const { apiKey, agentId, config } = await loadAuthOrFail();
43957
+ const manifest = loadManifest();
43958
+ const apps = getPrimitivesByKind(manifest, PrimitiveKind.WEB_APP);
43959
+ if (apps.length === 0) {
43960
+ console.error("\u274C No web apps found in compiled output.");
43961
+ console.log("\u{1F4A1} Define one with defineWebApp and list it under `webApps` on your LuaAgent.");
43962
+ return;
43963
+ }
43964
+ const app = await selectEntityOrPrompt({
43965
+ items: apps,
43966
+ entityName,
43967
+ entityType: "Web app",
43968
+ idProperty: "name",
43969
+ nameProperty: "name",
43970
+ promptMessage: "Select a web app:",
43971
+ formatChoice: /* @__PURE__ */ __name((a) => `${a.name} (${a.routes.length} route(s))`, "formatChoice"),
43972
+ asJson
43973
+ });
43974
+ if (!app) {
43975
+ console.log("Test cancelled.");
43976
+ return;
43977
+ }
43978
+ let spec = routeSpec;
43979
+ if (!spec) {
43980
+ if (asJson) {
43981
+ throw CliError.usage("--route is required with --json, e.g. --route 'GET /tickets?status=open'");
43982
+ }
43983
+ const answer = await safePrompt([
43984
+ {
43985
+ type: "input",
43986
+ name: "route",
43987
+ message: "Route to call (METHOD /path?query):",
43988
+ default: app.routes[0] ? `${app.routes[0].method} ${app.routes[0].path}` : "GET /"
43989
+ }
43990
+ ]);
43991
+ if (!answer) {
43992
+ console.log("Test cancelled.");
43993
+ return;
43994
+ }
43995
+ spec = answer.route;
43996
+ }
43997
+ let parsed;
43998
+ try {
43999
+ parsed = parseRouteSpec(spec);
44000
+ } catch (error) {
44001
+ throw CliError.usage(error.message);
44002
+ }
44003
+ const matched = matchManifestRoute(app.routes, parsed.method, parsed.path);
44004
+ if (!matched && !app.hasFetch) {
44005
+ throw CliError.usage(`No route of ${app.name} matches ${parsed.method} ${parsed.path}.`, [
44006
+ "Routes:",
44007
+ ...app.routes.map((r) => ` ${r.key}`)
44008
+ ].join("\n"));
44009
+ }
44010
+ const input = inputJson ? parseJsonInputOrFail(inputJson, '{"body": {...}, "headers": {"x-custom": "..."}}') : {};
44011
+ const auth = await developerAuth(apiKey, agentId, config);
44012
+ const yamlApp = config?.webApps?.find((a) => a.name === app.name);
44013
+ const webRouteInput = buildWebRouteInput({
44014
+ spec: parsed,
44015
+ route: matched,
44016
+ body: input.body,
44017
+ headers: input.headers,
44018
+ auth,
44019
+ app: {
44020
+ id: yamlApp?.webAppId || "local",
44021
+ name: app.name,
44022
+ version: yamlApp?.version || "0.0.0"
44023
+ }
44024
+ });
44025
+ progress(asJson, `
44026
+ \u{1F680} ${parsed.method} ${parsed.path}${matched ? ` \u2192 ${matched.key}` : " \u2192 fetch"}`);
44027
+ const appCode = loadArtifact(app, process.cwd());
44028
+ await withSandboxEnv(async () => {
44029
+ try {
44030
+ const result = await executeWebRoute({
44031
+ appCode,
44032
+ input: webRouteInput,
44033
+ apiKey,
44034
+ agentId
44035
+ });
44036
+ if (asJson) {
44037
+ console.log(JSON.stringify(result, null, 2));
44038
+ } else {
44039
+ const ok = result.status < 400;
44040
+ (ok ? success : (_, m) => console.error(m))(asJson, `
44041
+ ${ok ? "\u2705" : "\u26A0\uFE0F "} ${result.status}`);
44042
+ for (const [name, value3] of Object.entries(result.headers ?? {})) console.log(`${name}: ${value3}`);
44043
+ if (result.body) {
44044
+ console.log(result.body.kind === "json" ? JSON.stringify(result.body.value, null, 2) : result.body.kind === "text" ? result.body.value : `<${Buffer.from(result.body.value, "base64").length} bytes, base64>`);
44045
+ }
44046
+ }
44047
+ if (result.status >= 500) process.exitCode = 1;
44048
+ } catch (error) {
44049
+ handleExecutionError(error, "Web app route", asJson);
44050
+ }
44051
+ });
44052
+ }
44053
+ __name(testWebApp, "testWebApp");
42927
44054
  async function testJob(entityName, asJson = false) {
42928
44055
  progress(asJson, "\u23F0 Testing job...");
42929
44056
  progress(asJson, "\u{1F4E6} Compiling code first...");
@@ -44487,10 +45614,11 @@ async function pushVersionedPrimitive(handler, options = {}) {
44487
45614
  }
44488
45615
  const manifest = loadManifest();
44489
45616
  const bundleAccumulator = /* @__PURE__ */ new Map();
44490
- const pushData = handler.prepareForPush(manifest, selected.name, void 0, bundleAccumulator);
44491
- if (!pushData) {
45617
+ const prepared = handler.prepareForPush(manifest, selected.name, void 0, bundleAccumulator);
45618
+ if (!prepared) {
44492
45619
  throw new Error(`${handler.displayName} "${selected.name}" not found in compiled output.`);
44493
45620
  }
45621
+ const pushData = handler.completePushData ? await handler.completePushData(prepared, manifest, selected.name, bundleAccumulator) : prepared;
44494
45622
  let bundleStats = {
44495
45623
  total: 0,
44496
45624
  alreadyExisted: 0,
@@ -44520,6 +45648,12 @@ async function pushVersionedPrimitive(handler, options = {}) {
44520
45648
  writeSuccess(`
44521
45649
  \u2705 Successfully pushed ${selected.name} v${confirmedVersion}
44522
45650
  `);
45651
+ if (handler.kind === PrimitiveKind.WEB_APP) {
45652
+ return {
45653
+ productionDeploySucceeded: false,
45654
+ bundleStats
45655
+ };
45656
+ }
44523
45657
  if (await shouldDeployAfterPush(options)) {
44524
45658
  const deployedOk = await genericDeployAfterPush(handler, apiKey, agentId, entityId, selected.name, confirmedVersion, options.autoDeploy || false);
44525
45659
  return {
@@ -44666,6 +45800,8 @@ async function pushCommand(type, cmdObj) {
44666
45800
  versionedPushDeployResult = await pushVersionedPrimitive(voiceHandler, options);
44667
45801
  } else if (selectedType === "workflow") {
44668
45802
  versionedPushDeployResult = await pushVersionedPrimitive(workflowHandler, options);
45803
+ } else if (selectedType === "webapp") {
45804
+ versionedPushDeployResult = await pushVersionedPrimitive(webAppHandler, options);
44669
45805
  } else if (selectedType === "mcp") {
44670
45806
  const mcpActivated = await pushMCPServer(options);
44671
45807
  versionedPushDeployResult = {
@@ -44785,6 +45921,21 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
44785
45921
  });
44786
45922
  } else if (selectedType === "backup") {
44787
45923
  } else if (selectedType === "agent") {
45924
+ } else if (selectedType === "webapp") {
45925
+ writeHintBlock({
45926
+ headline: `Staged but NOT live yet. ${WEB_APP_GOES_LIVE_WITH_AGENT_VERSION}`,
45927
+ lines: [
45928
+ {
45929
+ label: "Snapshot:",
45930
+ command: "lua version create"
45931
+ },
45932
+ {
45933
+ label: "Go live:",
45934
+ command: "lua version promote"
45935
+ }
45936
+ ],
45937
+ when: "success"
45938
+ });
44788
45939
  } else if (deployHint) {
44789
45940
  writeHintBlock({
44790
45941
  headline: "Staged but NOT live yet. To make it active:",
@@ -45067,7 +46218,8 @@ async function pushAllCommand(options) {
45067
46218
  preprocessor: "\u{1F4E5}",
45068
46219
  postprocessor: "\u{1F4E4}",
45069
46220
  device: "\u{1F4E1}",
45070
- voice: "\u{1F399}\uFE0F"
46221
+ voice: "\u{1F399}\uFE0F",
46222
+ "web-app": "\u{1F5A5}\uFE0F"
45071
46223
  };
45072
46224
  const handlers = [
45073
46225
  skillHandler,
@@ -45083,7 +46235,10 @@ async function pushAllCommand(options) {
45083
46235
  postprocessorHandler,
45084
46236
  deviceHandler,
45085
46237
  deviceTriggerHandler,
45086
- voiceHandler
46238
+ voiceHandler,
46239
+ // Lua Apps: pushed (route bundle + page build) like any versioned kind, never deployed here —
46240
+ // a web app version goes live with the agent version that pins it.
46241
+ webAppHandler
45087
46242
  ];
45088
46243
  const handlerItems = handlers.map((handler) => ({
45089
46244
  handler,
@@ -45117,11 +46272,12 @@ ${icon} Pushing ${items.length} ${handler.displayName}(s)...`);
45117
46272
  for (const item of items) {
45118
46273
  try {
45119
46274
  const bundleAccumulator = /* @__PURE__ */ new Map();
45120
- const pushData = handler.prepareForPush(manifest, item.name, void 0, bundleAccumulator);
45121
- if (!pushData) {
46275
+ const prepared = handler.prepareForPush(manifest, item.name, void 0, bundleAccumulator);
46276
+ if (!prepared) {
45122
46277
  console.warn(`\u26A0\uFE0F ${handler.displayName} "${item.name}" not found in compiled output, skipping`);
45123
46278
  continue;
45124
46279
  }
46280
+ const pushData = handler.completePushData ? await handler.completePushData(prepared, manifest, item.name, bundleAccumulator) : prepared;
45125
46281
  if (bundleAccumulator.size > 0) {
45126
46282
  const stats = await ensureBundlesUploaded(apiKey, agentId, bundleAccumulator);
45127
46283
  bundleStatsAggregate.total += stats.total;
@@ -45179,7 +46335,8 @@ ${icon} Pushing ${items.length} ${handler.displayName}(s)...`);
45179
46335
  entityId
45180
46336
  });
45181
46337
  writeSuccess(` \u2705 ${item.name} v${newVersion} pushed`);
45182
- if (options.autoDeploy) {
46338
+ if (handler.kind === PrimitiveKind.WEB_APP) {
46339
+ } else if (options.autoDeploy) {
45183
46340
  toDeploy.push({
45184
46341
  handler,
45185
46342
  name: item.name,
@@ -45318,7 +46475,8 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
45318
46475
  jobs: "\u23F0",
45319
46476
  preprocessors: "\u{1F4E5}",
45320
46477
  postprocessors: "\u{1F4E4}",
45321
- voices: "\u{1F399}\uFE0F"
46478
+ voices: "\u{1F399}\uFE0F",
46479
+ "web apps": "\u{1F5A5}\uFE0F"
45322
46480
  };
45323
46481
  for (const [kind, items] of byKind) {
45324
46482
  const icon = summaryIcons[kind] || "\u{1F4E6}";
@@ -47681,8 +48839,8 @@ init_cli();
47681
48839
  init_constants();
47682
48840
  init_command_utils();
47683
48841
  init_developer_api_service();
47684
- import fs18 from "fs";
47685
- import path25 from "path";
48842
+ import fs19 from "fs";
48843
+ import path27 from "path";
47686
48844
  import inquirer10 from "inquirer";
47687
48845
  init_analytics();
47688
48846
  init_cli_error();
@@ -48031,10 +49189,10 @@ function variablesToEnvContent(variables) {
48031
49189
  }
48032
49190
  __name(variablesToEnvContent, "variablesToEnvContent");
48033
49191
  function saveSandboxEnvVariables(variables) {
48034
- const envFilePath = path25.join(process.cwd(), ".env");
49192
+ const envFilePath = path27.join(process.cwd(), ".env");
48035
49193
  try {
48036
49194
  const content = variablesToEnvContent(variables);
48037
- fs18.writeFileSync(envFilePath, content, "utf8");
49195
+ fs19.writeFileSync(envFilePath, content, "utf8");
48038
49196
  return true;
48039
49197
  } catch (error) {
48040
49198
  console.error("\u274C Error saving .env file:", error);
@@ -50490,6 +51648,7 @@ async function listChannelsInteractive(agentApi, agentId, config) {
50490
51648
  clearPromptLines(2);
50491
51649
  const selectedChannel = channels[selectedIndex];
50492
51650
  displayChannelDetails(selectedChannel);
51651
+ if (selectedChannel.type === "slack") await slackConnectionActions(agentApi, agentId, selectedChannel);
50493
51652
  }
50494
51653
  __name(listChannelsInteractive, "listChannelsInteractive");
50495
51654
  function displayChannelDetails(channel) {
@@ -50617,6 +51776,20 @@ function displaySlackDetails(channel) {
50617
51776
  if (channel.data.botId) {
50618
51777
  writeInfo(`Bot ID: ${channel.data.botId}`);
50619
51778
  }
51779
+ const status = channel.data.status;
51780
+ if (status === "uninstalled") {
51781
+ writeInfo("Status: uninstalled in Slack \u2014 reinstall the app to reconnect");
51782
+ } else if (status === "pending_setup" || status === "pending_oauth") {
51783
+ writeInfo("Status: not installed yet \u2014 get an install link below");
51784
+ }
51785
+ const setup = channel.data.setup;
51786
+ if (setup) {
51787
+ writeInfo(`Signing secret: ${SLACK_SIGNING_LABELS[setup.signing]}`);
51788
+ if (setup.updateAvailable) {
51789
+ const missing = setup.missingRequired.length ? ` (missing: ${setup.missingRequired.join(", ")})` : "";
51790
+ writeInfo(`Update: available${missing} \u2014 choose "Show the update steps" below`);
51791
+ }
51792
+ }
50620
51793
  }
50621
51794
  }
50622
51795
  __name(displaySlackDetails, "displaySlackDetails");
@@ -50888,8 +52061,97 @@ To complete the email channel setup, configure email forwarding:`);
50888
52061
  }
50889
52062
  }
50890
52063
  __name(createEmailChannel, "createEmailChannel");
52064
+ var SLACK_SIGNING_SECRET = /^[0-9A-Za-z]{32}$/;
52065
+ var SLACK_SIGNING_LABELS = {
52066
+ none: "not saved \u2014 Slack events are not verified",
52067
+ verifying: "saved, waiting for the first event to confirm it",
52068
+ mismatch: "saved, but Slack's events don't match it \u2014 paste it again",
52069
+ failing: "an event didn't match it and was rejected \u2014 if you regenerated it in Slack, paste the new one; otherwise this clears with the next real message",
52070
+ verified: "verified \u2014 events that fail the check are rejected"
52071
+ };
52072
+ function slackSigningSecretPrompt() {
52073
+ return {
52074
+ type: "password",
52075
+ name: "signingSecret",
52076
+ message: "Enter the Slack app Signing Secret (Basic Information \u2192 App Credentials):",
52077
+ mask: "*",
52078
+ validate: /* @__PURE__ */ __name((input, answers) => {
52079
+ const value3 = input.trim();
52080
+ if (!SLACK_SIGNING_SECRET.test(value3)) return "The signing secret is 32 letters and digits";
52081
+ if (answers?.clientSecret && value3 === answers.clientSecret.trim()) {
52082
+ return "That's the Client Secret. Paste the Signing Secret, just below it";
52083
+ }
52084
+ return true;
52085
+ }, "validate")
52086
+ };
52087
+ }
52088
+ __name(slackSigningSecretPrompt, "slackSigningSecretPrompt");
52089
+ async function offerToOpen(url, question) {
52090
+ const { openIt } = await inquirer12.prompt([
52091
+ {
52092
+ type: "confirm",
52093
+ name: "openIt",
52094
+ message: question,
52095
+ default: true
52096
+ }
52097
+ ]);
52098
+ if (openIt) await open5(url);
52099
+ }
52100
+ __name(offerToOpen, "offerToOpen");
52101
+ async function startSlackApp(agentApi, agentId, kind) {
52102
+ const { source } = await inquirer12.prompt([
52103
+ {
52104
+ type: "list",
52105
+ name: "source",
52106
+ message: "Which Slack app?",
52107
+ choices: [
52108
+ {
52109
+ name: "Create a new app from Lua\u2019s manifest (recommended)",
52110
+ value: "new"
52111
+ },
52112
+ {
52113
+ name: "I already have a Slack app",
52114
+ value: "existing"
52115
+ }
52116
+ ]
52117
+ }
52118
+ ]);
52119
+ if (source === "existing") return true;
52120
+ const response = await agentApi.getSlackManifest(agentId);
52121
+ const createAppUrl = response.success ? response.data?.createAppUrl : void 0;
52122
+ if (!createAppUrl) {
52123
+ throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Could not prepare the Slack app", response.error?.error);
52124
+ }
52125
+ writeInfo("\n1. Create the app. Slack opens with it named after your agent, with its permissions and events.");
52126
+ writeInfo(" Pick the workspace, then click Next and Create and Install:");
52127
+ console.log(`
52128
+ ${createAppUrl}
52129
+ `);
52130
+ await offerToOpen(createAppUrl, "Open it in your browser?");
52131
+ writeInfo(" If Slack says the Request URL didn't respond, click Retry under Event Subscriptions.");
52132
+ if (kind === "private") {
52133
+ writeInfo("2. In the new app: Install App \u2192 Install to Workspace \u2192 Allow, then copy the Bot User OAuth Token.");
52134
+ writeInfo("3. Basic Information \u2192 App Credentials: copy the Signing Secret.\n");
52135
+ } else {
52136
+ writeInfo("2. Basic Information \u2192 App Credentials: copy the App ID, Client ID, Client Secret and Signing Secret.\n");
52137
+ }
52138
+ return false;
52139
+ }
52140
+ __name(startSlackApp, "startSlackApp");
52141
+ var slackManifestEditorUrl = /* @__PURE__ */ __name((appId, teamId) => teamId ? `https://app.slack.com/app-settings/${teamId}/${appId}/app-manifest` : `https://api.slack.com/apps/${appId}`, "slackManifestEditorUrl");
52142
+ function printManifestUpdate(manifestUrl, manifest) {
52143
+ writeInfo("\nUpdate the app with Lua\u2019s manifest:");
52144
+ writeInfo(` 1. Open ${manifestUrl}`);
52145
+ writeInfo(" 2. Paste this on the JSON tab and click Save Changes");
52146
+ writeInfo(" 3. Reinstall the app when Slack asks");
52147
+ writeInfo(" Saving replaces the app's settings in Slack: add anything you set up there yourself (slash commands, extra permissions) to the manifest first.\n");
52148
+ console.log(JSON.stringify(manifest, null, 2));
52149
+ console.log();
52150
+ }
52151
+ __name(printManifestUpdate, "printManifestUpdate");
50891
52152
  async function createSlackPrivateChannel(agentApi, agentId) {
50892
52153
  writeInfo("\u{1F512} Creating Private Slack Channel\n");
52154
+ const existingApp = await startSlackApp(agentApi, agentId, "private");
50893
52155
  const answers = await inquirer12.prompt([
50894
52156
  {
50895
52157
  type: "password",
@@ -50905,13 +52167,15 @@ async function createSlackPrivateChannel(agentApi, agentId) {
50905
52167
  }
50906
52168
  return true;
50907
52169
  }, "validate")
50908
- }
52170
+ },
52171
+ slackSigningSecretPrompt()
50909
52172
  ]);
50910
52173
  const channelData = {
50911
52174
  type: "slack",
50912
52175
  slack: {
50913
52176
  type: "private",
50914
- accessToken: answers.accessToken.trim()
52177
+ accessToken: answers.accessToken.trim(),
52178
+ signingSecret: answers.signingSecret.trim()
50915
52179
  }
50916
52180
  };
50917
52181
  writeInfo("\n\u{1F4E1} Creating Slack private channel...");
@@ -50920,19 +52184,16 @@ async function createSlackPrivateChannel(agentApi, agentId) {
50920
52184
  throw channelCreateError(response.error, "This Slack workspace is already connected to this agent.");
50921
52185
  }
50922
52186
  const data = response.data;
50923
- if ("slack" in data && "botName" in data.slack) {
50924
- writeSuccess("\n\u2705 Slack private channel created successfully!\n");
50925
- writeInfo(`\u{1F916} Bot Name: ${data.slack.botName}`);
50926
- writeInfo(`\u{1F3E2} Workspace: ${data.slack.url}`);
50927
- writeInfo(`\u{1F194} Bot ID: ${data.slack.botId}`);
50928
- writeInfo(`Ensure your app Manifest is configured correctly. Below is an example:
50929
- `);
50930
- writeInfo(JSON.stringify(data.slack.manifest, null, 2));
50931
- }
52187
+ writeSuccess("\n\u2705 Slack private channel created successfully!\n");
52188
+ writeInfo(`\u{1F916} Bot Name: ${data.botName}`);
52189
+ writeInfo(`\u{1F3E2} Workspace: ${data.teamName || data.url}`);
52190
+ if (existingApp) printManifestUpdate(slackManifestEditorUrl(data.appId, data.teamId), data.manifest);
52191
+ writeInfo("\u{1F4AC} Send the bot a message in Slack: the first event confirms the signing secret.");
50932
52192
  }
50933
52193
  __name(createSlackPrivateChannel, "createSlackPrivateChannel");
50934
52194
  async function createSlackPublicChannel(agentApi, agentId) {
50935
52195
  writeInfo("\u{1F310} Creating Public Slack Channel\n");
52196
+ const existingApp = await startSlackApp(agentApi, agentId, "public");
50936
52197
  const answers = await inquirer12.prompt([
50937
52198
  {
50938
52199
  type: "input",
@@ -50955,10 +52216,9 @@ async function createSlackPublicChannel(agentApi, agentId) {
50955
52216
  name: "clientSecret",
50956
52217
  message: "Enter Slack client secret:",
50957
52218
  mask: "*",
50958
- validate: /* @__PURE__ */ __name((input) => {
50959
- return input.trim().length > 0 || "Client secret is required";
50960
- }, "validate")
50961
- }
52219
+ validate: /* @__PURE__ */ __name((input) => input.trim().length > 0 || "Client secret is required", "validate")
52220
+ },
52221
+ slackSigningSecretPrompt()
50962
52222
  ]);
50963
52223
  const channelData = {
50964
52224
  type: "slack",
@@ -50966,7 +52226,8 @@ async function createSlackPublicChannel(agentApi, agentId) {
50966
52226
  type: "public",
50967
52227
  appId: answers.appId.trim(),
50968
52228
  clientId: answers.clientId.trim(),
50969
- clientSecret: answers.clientSecret.trim()
52229
+ clientSecret: answers.clientSecret.trim(),
52230
+ signingSecret: answers.signingSecret.trim()
50970
52231
  }
50971
52232
  };
50972
52233
  writeInfo("\n\u{1F4E1} Creating Slack public channel...");
@@ -50975,19 +52236,84 @@ async function createSlackPublicChannel(agentApi, agentId) {
50975
52236
  throw channelCreateError(response.error, "This Slack app is already connected to this agent.");
50976
52237
  }
50977
52238
  const data = response.data;
50978
- if ("redirectUri" in data) {
50979
- writeSuccess("\n\u2705 Slack public channel created successfully!\n");
50980
- writeInfo(`\u{1F194} App ID: ${data.appId}`);
50981
- writeInfo(`\u{1F517} Redirect URI: ${data.redirectUri}`);
50982
- writeInfo(`\u{1F517} Webhook URL: ${data.webhookUrl}
52239
+ writeSuccess("\n\u2705 Slack public channel created successfully!\n");
52240
+ writeInfo(`\u{1F194} App ID: ${data.appId}`);
52241
+ if (existingApp) printManifestUpdate(slackManifestEditorUrl(data.appId), data.manifest);
52242
+ writeInfo("Last step: install the app in your workspace (the link works for 30 minutes):");
52243
+ console.log(`
52244
+ ${data.installUrl}
50983
52245
  `);
50984
- writeInfo(`\u{1F517} Install URL: https://auth.heylua.ai/slack/install/${data.appId}`);
50985
- writeInfo(`Ensure your app Manifest is configured correctly. Below is an example:
52246
+ await offerToOpen(data.installUrl, "Open it in your browser?");
52247
+ writeInfo("After you allow it, Slack shows Lua's result page. For a new link later: lua channels \u2192 List \u2192 this connection.");
52248
+ }
52249
+ __name(createSlackPublicChannel, "createSlackPublicChannel");
52250
+ async function slackConnectionActions(agentApi, agentId, channel) {
52251
+ if (!channel.id) return;
52252
+ const choices = [
52253
+ {
52254
+ name: "Show the update steps and manifest",
52255
+ value: "manifest"
52256
+ },
52257
+ {
52258
+ name: "Add or replace the signing secret",
52259
+ value: "secret"
52260
+ },
52261
+ {
52262
+ name: "Re-check permissions with Slack",
52263
+ value: "check"
52264
+ }
52265
+ ];
52266
+ if (channel.data?.type === "public") choices.push({
52267
+ name: "Get an install link",
52268
+ value: "install"
52269
+ });
52270
+ choices.push({
52271
+ name: "\u2190 Back",
52272
+ value: "back"
52273
+ });
52274
+ const { action } = await inquirer12.prompt([
52275
+ {
52276
+ type: "list",
52277
+ name: "action",
52278
+ message: "What would you like to do?",
52279
+ choices
52280
+ }
52281
+ ]);
52282
+ if (action === "manifest") {
52283
+ const response = await agentApi.getSlackManifest(agentId, channel.id);
52284
+ if (!response.success || !response.data) throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Could not load the manifest");
52285
+ printManifestUpdate(response.data.manifestUrl ?? slackManifestEditorUrl(channel.data.appId, channel.data.teamId), response.data.manifest);
52286
+ writeInfo("Then save the signing secret here and check again.");
52287
+ } else if (action === "secret") {
52288
+ const { signingSecret } = await inquirer12.prompt([
52289
+ slackSigningSecretPrompt()
52290
+ ]);
52291
+ const response = await agentApi.saveSlackSigningSecret(agentId, channel.identifier, signingSecret.trim());
52292
+ if (!response.success) throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Could not save the signing secret");
52293
+ writeSuccess("\u2705 Signing secret saved. The next message the bot receives confirms it.");
52294
+ } else if (action === "check") {
52295
+ const response = await agentApi.checkSlackConnection(agentId, channel.id);
52296
+ if (!response.success || !response.data) throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Could not reach Slack");
52297
+ const { status, setup } = response.data;
52298
+ if (status === "uninstalled") {
52299
+ writeInfo("\u26A0\uFE0F Slack says this app is no longer installed. Reinstall it, then reconnect.");
52300
+ } else if (setup.missingRequired.length) {
52301
+ writeInfo(`\u26A0\uFE0F Missing required permissions: ${setup.missingRequired.join(", ")}. Update the manifest and reinstall.`);
52302
+ } else {
52303
+ writeSuccess(`\u2705 Slack answered. Signing secret: ${SLACK_SIGNING_LABELS[setup.signing]}.`);
52304
+ }
52305
+ if (setup.missingOptional.length) writeInfo(`Optional permissions not granted: ${setup.missingOptional.join(", ")}.`);
52306
+ } else if (action === "install") {
52307
+ const response = await agentApi.getSlackInstallUrl(agentId, channel.id);
52308
+ if (!response.success || !response.data) throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Could not get an install link");
52309
+ writeInfo("Install link (works for 30 minutes):");
52310
+ console.log(`
52311
+ ${response.data.installUrl}
50986
52312
  `);
50987
- writeInfo(JSON.stringify(data.manifest, null, 2));
52313
+ await offerToOpen(response.data.installUrl, "Open it in your browser?");
50988
52314
  }
50989
52315
  }
50990
- __name(createSlackPublicChannel, "createSlackPublicChannel");
52316
+ __name(slackConnectionActions, "slackConnectionActions");
50991
52317
 
50992
52318
  // src/commands/logs.ts
50993
52319
  init_cli();
@@ -51922,8 +53248,8 @@ var DrainsApi = class extends HttpClient {
51922
53248
  * answer is a plain `LogDrainRead` and is safe to retry; minting is not.
51923
53249
  */
51924
53250
  async rotateSecret(drainId, finalize) {
51925
- const path27 = this.drainPath(drainId, `/rotate-secret${finalize ? "?finalize=true" : ""}`);
51926
- return finalize ? this.httpPost(path27, {}) : this.httpPostOnce(path27, {});
53251
+ const path30 = this.drainPath(drainId, `/rotate-secret${finalize ? "?finalize=true" : ""}`);
53252
+ return finalize ? this.httpPost(path30, {}) : this.httpPostOnce(path30, {});
51927
53253
  }
51928
53254
  /**
51929
53255
  * Re-enqueue one batch's records (PRO-2126).
@@ -52003,8 +53329,8 @@ function printJson(payload) {
52003
53329
  console.log(JSON.stringify(payload, null, 2));
52004
53330
  }
52005
53331
  __name(printJson, "printJson");
52006
- function emitAside(line, json) {
52007
- if (json) process.stderr.write(`${line}
53332
+ function emitAside(line, json2) {
53333
+ if (json2) process.stderr.write(`${line}
52008
53334
  `);
52009
53335
  else writeInfo(line);
52010
53336
  }
@@ -52429,48 +53755,48 @@ function verificationCurl(type, endpoint, headers) {
52429
53755
  ];
52430
53756
  }
52431
53757
  __name(verificationCurl, "verificationCurl");
52432
- function bucketVerificationInstructions(drain, json) {
53758
+ function bucketVerificationInstructions(drain, json2) {
52433
53759
  const prefix = drain.bucket?.prefix;
52434
53760
  const where = prefix ? `${prefix}/_lua-verify-*` : "_lua-verify-*";
52435
53761
  const bucket = drain.bucket?.bucket ?? "<your bucket>";
52436
53762
  const endpointArg = drain.bucket?.endpoint ? ` --endpoint-url '${drain.bucket.endpoint}'` : "";
52437
- emitAside("Lua writes a challenge object into the bucket itself \u2014 nothing is POSTed to a URL.", json);
52438
- emitAside("Check the credentials can write under the prefix first:", json);
52439
- emitAside("", json);
52440
- emitAside(` aws s3 cp - 's3://${bucket}/${prefix ? `${prefix}/` : ""}_lua-write-check'${endpointArg} </dev/null`, json);
52441
- emitAside("", json);
52442
- emitAside(" (a zero-byte object, yours to delete \u2014 a write-only role cannot remove it itself)", json);
52443
- emitAside("", json);
52444
- emitAside(`Then: lua drains verify ${drain.id}`, json);
52445
- emitAside("", json);
52446
- emitAside("That is what mints the challenge, and how it settles depends on the role:", json);
52447
- emitAside(" \xB7 with s3:GetObject, Lua reads the object back and 'verify' finishes on its own;", json);
52448
- emitAside(" \xB7 write-only \u2014 the grant we recommend \u2014 leaves the claim open. List the prefix", json);
52449
- emitAside(` for ${where}, take the <token> off the object name, and run:`, json);
52450
- emitAside("", json);
52451
- emitAside(` lua drains confirm ${drain.id} --token <token>`, json);
53763
+ emitAside("Lua writes a challenge object into the bucket itself \u2014 nothing is POSTed to a URL.", json2);
53764
+ emitAside("Check the credentials can write under the prefix first:", json2);
53765
+ emitAside("", json2);
53766
+ emitAside(` aws s3 cp - 's3://${bucket}/${prefix ? `${prefix}/` : ""}_lua-write-check'${endpointArg} </dev/null`, json2);
53767
+ emitAside("", json2);
53768
+ emitAside(" (a zero-byte object, yours to delete \u2014 a write-only role cannot remove it itself)", json2);
53769
+ emitAside("", json2);
53770
+ emitAside(`Then: lua drains verify ${drain.id}`, json2);
53771
+ emitAside("", json2);
53772
+ emitAside("That is what mints the challenge, and how it settles depends on the role:", json2);
53773
+ emitAside(" \xB7 with s3:GetObject, Lua reads the object back and 'verify' finishes on its own;", json2);
53774
+ emitAside(" \xB7 write-only \u2014 the grant we recommend \u2014 leaves the claim open. List the prefix", json2);
53775
+ emitAside(` for ${where}, take the <token> off the object name, and run:`, json2);
53776
+ emitAside("", json2);
53777
+ emitAside(` lua drains confirm ${drain.id} --token <token>`, json2);
52452
53778
  }
52453
53779
  __name(bucketVerificationInstructions, "bucketVerificationInstructions");
52454
53780
  function verificationInstructions(drain, headers, options) {
52455
53781
  const type = drain.type;
52456
- const json = options.json;
52457
- emitAside("", json);
52458
- emitAside("Before anything is delivered, Lua has to confirm you own this destination.", json);
53782
+ const json2 = options.json;
53783
+ emitAside("", json2);
53784
+ emitAside("Before anything is delivered, Lua has to confirm you own this destination.", json2);
52459
53785
  if (type === "s3") {
52460
- bucketVerificationInstructions(drain, json);
53786
+ bucketVerificationInstructions(drain, json2);
52461
53787
  return;
52462
53788
  }
52463
53789
  if (TOKEN_ECHO_TYPES.includes(type)) {
52464
- emitAside("Lua POSTs one record carrying an X-Lua-Verify header; your receiver must answer 2xx AND", json);
52465
- emitAside("repeat that header back on the response. Check yours first:", json);
53790
+ emitAside("Lua POSTs one record carrying an X-Lua-Verify header; your receiver must answer 2xx AND", json2);
53791
+ emitAside("repeat that header back on the response. Check yours first:", json2);
52466
53792
  } else {
52467
- emitAside("Lua POSTs one record to the intake; any 2xx from the vendor verifies the drain.", json);
52468
- emitAside("Check the credentials reach the intake first:", json);
53793
+ emitAside("Lua POSTs one record to the intake; any 2xx from the vendor verifies the drain.", json2);
53794
+ emitAside("Check the credentials reach the intake first:", json2);
52469
53795
  }
52470
- emitAside("", json);
52471
- for (const line of verificationCurl(type, drain.endpoint, headers)) emitAside(` ${line}`, json);
52472
- emitAside("", json);
52473
- emitAside(`Then: lua drains verify ${drain.id}`, json);
53796
+ emitAside("", json2);
53797
+ for (const line of verificationCurl(type, drain.endpoint, headers)) emitAside(` ${line}`, json2);
53798
+ emitAside("", json2);
53799
+ emitAside(`Then: lua drains verify ${drain.id}`, json2);
52474
53800
  }
52475
53801
  __name(verificationInstructions, "verificationInstructions");
52476
53802
  async function createDrainCore(context, options) {
@@ -54662,7 +55988,7 @@ import chalk3 from "chalk";
54662
55988
  import { existsSync as existsSync10, readFileSync as readFileSync13 } from "fs";
54663
55989
  import { join as join10 } from "path";
54664
55990
  import { performance } from "perf_hooks";
54665
- import * as os from "os";
55991
+ import * as os2 from "os";
54666
55992
  init_semver();
54667
55993
  init_version_check();
54668
55994
  init_constants();
@@ -54702,9 +56028,9 @@ function gatherEnvironment(currentVersion) {
54702
56028
  cliVersion: currentVersion,
54703
56029
  channel: detectChannel(currentVersion),
54704
56030
  nodeVersion: process.version,
54705
- platform: os.platform(),
54706
- arch: os.arch(),
54707
- osRelease: os.release(),
56031
+ platform: os2.platform(),
56032
+ arch: os2.arch(),
56033
+ osRelease: os2.release(),
54708
56034
  installMethod: detectInstallMethod(process.execPath),
54709
56035
  execPath: process.execPath,
54710
56036
  configDir: CLI_CONFIG_DIR,
@@ -56220,12 +57546,12 @@ function resolveTrigger(triggers, ref) {
56220
57546
  return trigger;
56221
57547
  }
56222
57548
  __name(resolveTrigger, "resolveTrigger");
56223
- function displayTriggersCore(triggers, json) {
57549
+ function displayTriggersCore(triggers, json2) {
56224
57550
  trackEvent("cli_triggers_list", {
56225
57551
  triggers_count: triggers.length,
56226
- json_output: json
57552
+ json_output: json2
56227
57553
  });
56228
- if (json) {
57554
+ if (json2) {
56229
57555
  console.log(JSON.stringify(triggers.map((t) => ({
56230
57556
  id: t.id,
56231
57557
  name: t.name,
@@ -56373,7 +57699,7 @@ function snippet(value3, maxLength = 100) {
56373
57699
  return text.length > maxLength ? `${text.substring(0, maxLength)}...` : text;
56374
57700
  }
56375
57701
  __name(snippet, "snippet");
56376
- async function fetchAndDisplayLogsCore(context, trigger, limit, json) {
57702
+ async function fetchAndDisplayLogsCore(context, trigger, limit, json2) {
56377
57703
  const triggerApi = triggerApiFor(context);
56378
57704
  const response = await triggerApi.getTriggerExecutions(trigger.id, {
56379
57705
  limit
@@ -56381,7 +57707,7 @@ async function fetchAndDisplayLogsCore(context, trigger, limit, json) {
56381
57707
  trackEvent("cli_triggers_logs", {
56382
57708
  trigger_name: trigger.name,
56383
57709
  limit,
56384
- json_output: json,
57710
+ json_output: json2,
56385
57711
  success: !!response.success
56386
57712
  });
56387
57713
  if (!response.success || !response.data) {
@@ -56389,7 +57715,7 @@ async function fetchAndDisplayLogsCore(context, trigger, limit, json) {
56389
57715
  return false;
56390
57716
  }
56391
57717
  const { executions = [], total } = response.data;
56392
- if (json) {
57718
+ if (json2) {
56393
57719
  console.log(JSON.stringify(response.data, null, 2));
56394
57720
  return true;
56395
57721
  }
@@ -57971,9 +59297,9 @@ init_lua_fetch();
57971
59297
  init_compiler();
57972
59298
  init_types();
57973
59299
  init_cli_error();
57974
- import fs19 from "fs";
57975
- import os2 from "os";
57976
- import path26 from "path";
59300
+ import fs20 from "fs";
59301
+ import os3 from "os";
59302
+ import path28 from "path";
57977
59303
  var EXPORT_NOT_COMPILABLE = "export-not-compilable";
57978
59304
  var EXPORT_CHECK_TSCONFIG = {
57979
59305
  compilerOptions: {
@@ -57994,13 +59320,13 @@ var EXPORT_CHECK_TSCONFIG = {
57994
59320
  ]
57995
59321
  };
57996
59322
  async function dryCompileWorkflowExport(files) {
57997
- const root = fs19.mkdtempSync(path26.join(os2.tmpdir(), "lua-workflows-export-"));
59323
+ const root = fs20.mkdtempSync(path28.join(os3.tmpdir(), "lua-workflows-export-"));
57998
59324
  const issues = [];
57999
59325
  const workflows = [];
58000
59326
  try {
58001
- fs19.writeFileSync(path26.join(root, "tsconfig.json"), `${JSON.stringify(EXPORT_CHECK_TSCONFIG, null, 2)}
59327
+ fs20.writeFileSync(path28.join(root, "tsconfig.json"), `${JSON.stringify(EXPORT_CHECK_TSCONFIG, null, 2)}
58002
59328
  `);
58003
- fs19.writeFileSync(path26.join(root, "package.json"), `${JSON.stringify({
59329
+ fs20.writeFileSync(path28.join(root, "package.json"), `${JSON.stringify({
58004
59330
  name: "lua-workflows-export-check",
58005
59331
  version: "0.0.0",
58006
59332
  private: true,
@@ -58008,23 +59334,23 @@ async function dryCompileWorkflowExport(files) {
58008
59334
  }, null, 2)}
58009
59335
  `);
58010
59336
  for (const f of files) {
58011
- const target = path26.resolve(root, f.path);
58012
- if (!target.startsWith(root + path26.sep)) continue;
58013
- fs19.mkdirSync(path26.dirname(target), {
59337
+ const target = path28.resolve(root, f.path);
59338
+ if (!target.startsWith(root + path28.sep)) continue;
59339
+ fs20.mkdirSync(path28.dirname(target), {
58014
59340
  recursive: true
58015
59341
  });
58016
- fs19.writeFileSync(target, f.contents);
59342
+ fs20.writeFileSync(target, f.contents);
58017
59343
  }
58018
59344
  const result = await new Compiler({
58019
59345
  rootDir: root,
58020
- outDir: path26.join(root, "dist-v2"),
59346
+ outDir: path28.join(root, "dist-v2"),
58021
59347
  minify: false,
58022
59348
  sourceMaps: false,
58023
59349
  quietWarnings: true,
58024
59350
  runtimeValidation: false
58025
59351
  }).compile();
58026
59352
  for (const e of result.errors) {
58027
- const file = e.file ? path26.relative(root, e.file) : void 0;
59353
+ const file = e.file ? path28.relative(root, e.file) : void 0;
58028
59354
  const where = file ? `${file}${e.line ? `:${e.line}` : ""}` : void 0;
58029
59355
  issues.push({
58030
59356
  code: EXPORT_NOT_COMPILABLE,
@@ -58059,7 +59385,7 @@ async function dryCompileWorkflowExport(files) {
58059
59385
  });
58060
59386
  } finally {
58061
59387
  if (debugEnabled()) console.error(`\u{1F41B} export check project kept at ${root}`);
58062
- else fs19.rmSync(root, {
59388
+ else fs20.rmSync(root, {
58063
59389
  recursive: true,
58064
59390
  force: true
58065
59391
  });
@@ -58114,7 +59440,7 @@ async function workflowsCommand(action, target, extra, cmdObj) {
58114
59440
  const options = {
58115
59441
  ...cmdObj ?? {}
58116
59442
  };
58117
- const json = !!options.json;
59443
+ const json2 = !!options.json;
58118
59444
  let resolved;
58119
59445
  if (action) {
58120
59446
  resolved = validateOrSuggest("workflows.action", action);
@@ -58157,14 +59483,14 @@ async function workflowsCommand(action, target, extra, cmdObj) {
58157
59483
  return;
58158
59484
  }
58159
59485
  const { agentId, orgId, apiKey } = await initializeCommand({
58160
- showProgress: !json
59486
+ showProgress: !json2
58161
59487
  });
58162
59488
  const ctx = {
58163
59489
  agentId,
58164
59490
  orgId,
58165
59491
  apiKey,
58166
59492
  api: new WorkflowApi(BASE_URLS.API, apiKey, agentId),
58167
- json
59493
+ json: json2
58168
59494
  };
58169
59495
  const code = await executeAction(ctx, resolved, target, extra, options);
58170
59496
  if (code !== void 0 && code !== 0) process.exitCode = code;
@@ -59914,15 +61240,15 @@ function actorLabel(by, now = Date.now()) {
59914
61240
  return Number.isFinite(at) ? `${who} (${agoFrom(at, now)})` : who;
59915
61241
  }
59916
61242
  __name(actorLabel, "actorLabel");
59917
- function reservedSecretKeyPaths(value3, path27 = "", depth = 0, out = []) {
61243
+ function reservedSecretKeyPaths(value3, path30 = "", depth = 0, out = []) {
59918
61244
  if (depth > 64 || out.length >= 20 || value3 === null || typeof value3 !== "object") return out;
59919
61245
  if (Array.isArray(value3)) {
59920
- value3.forEach((v, i) => reservedSecretKeyPaths(v, `${path27}[${i}]`, depth + 1, out));
61246
+ value3.forEach((v, i) => reservedSecretKeyPaths(v, `${path30}[${i}]`, depth + 1, out));
59921
61247
  return out;
59922
61248
  }
59923
61249
  for (const [k, v] of Object.entries(value3)) {
59924
61250
  if (out.length >= 20) break;
59925
- const child = path27 ? `${path27}.${k}` : k;
61251
+ const child = path30 ? `${path30}.${k}` : k;
59926
61252
  if (isWorkflowSecretKey(k) && v !== null && v !== void 0 && typeof v !== "object") out.push(child);
59927
61253
  else reservedSecretKeyPaths(v, child, depth + 1, out);
59928
61254
  }
@@ -60382,11 +61708,11 @@ async function exportCore(ctx, name, o) {
60382
61708
  if (!ctx.json && res.error?.statusCode === 404 && code === "VERSION_NOT_FOUND") console.error(`\u274C "${wf.name}" has no active version to export \u2014 pass -v <version|latest>`);
60383
61709
  return apiFailure(ctx, res, "export");
60384
61710
  }
60385
- const fs20 = await import("fs");
60386
- const path27 = await import("path");
60387
- const outDir = path27.resolve(o.out ?? ".");
61711
+ const fs22 = await import("fs");
61712
+ const path30 = await import("path");
61713
+ const outDir = path30.resolve(o.out ?? ".");
60388
61714
  const d = res.data;
60389
- const escaping = d.files.filter((f) => !path27.resolve(outDir, f.path).startsWith(outDir + path27.sep));
61715
+ const escaping = d.files.filter((f) => !path30.resolve(outDir, f.path).startsWith(outDir + path30.sep));
60390
61716
  if (escaping.length > 0) {
60391
61717
  if (ctx.json) {
60392
61718
  emitJson(ctx, {
@@ -60445,17 +61771,17 @@ async function exportCore(ctx, name, o) {
60445
61771
  }, null, 2));
60446
61772
  return WORKFLOW_EXIT.OK;
60447
61773
  }
60448
- const clash = d.files.filter((f) => fs20.existsSync(path27.join(outDir, f.path)));
61774
+ const clash = d.files.filter((f) => fs22.existsSync(path30.join(outDir, f.path)));
60449
61775
  if (clash.length > 0 && !o.force) {
60450
61776
  console.error(`\u274C export: ${clash.map((f) => f.path).join(", ")} already exist${clash.length === 1 ? "s" : ""} under ${outDir} \u2014 pass --force to overwrite, or --out <dir>`);
60451
61777
  return WORKFLOW_EXIT.API;
60452
61778
  }
60453
61779
  for (const f of d.files) {
60454
- const target = path27.join(outDir, f.path);
60455
- fs20.mkdirSync(path27.dirname(target), {
61780
+ const target = path30.join(outDir, f.path);
61781
+ fs22.mkdirSync(path30.dirname(target), {
60456
61782
  recursive: true
60457
61783
  });
60458
- fs20.writeFileSync(target, f.contents);
61784
+ fs22.writeFileSync(target, f.contents);
60459
61785
  console.log(` ${f.path}`);
60460
61786
  }
60461
61787
  const written = `exported "${wf.name}" v${d.version} (${d.form} form) \u2014 ${d.files.length} file(s) under ${outDir}`;
@@ -60495,9 +61821,9 @@ var archiveDownload = {
60495
61821
  }, "fetchBytes")
60496
61822
  };
60497
61823
  async function archiveRunsCore(ctx, o) {
60498
- const fs20 = await import("fs");
60499
- const path27 = await import("path");
60500
- const crypto5 = await import("crypto");
61824
+ const fs22 = await import("fs");
61825
+ const path30 = await import("path");
61826
+ const crypto6 = await import("crypto");
60501
61827
  const now = Date.now();
60502
61828
  const since = parseSinceFlag(o.since, now);
60503
61829
  if (!Number.isFinite(since)) {
@@ -60527,13 +61853,13 @@ async function archiveRunsCore(ctx, o) {
60527
61853
  if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
60528
61854
  workflowId = wf.id;
60529
61855
  }
60530
- fs20.mkdirSync(o.out, {
61856
+ fs22.mkdirSync(o.out, {
60531
61857
  recursive: true
60532
61858
  });
60533
- const indexFile = path27.join(o.out, "archive-index.ndjson");
61859
+ const indexFile = path30.join(o.out, "archive-index.ndjson");
60534
61860
  const indexed = /* @__PURE__ */ new Map();
60535
- if (fs20.existsSync(indexFile)) {
60536
- for (const line of fs20.readFileSync(indexFile, "utf8").split("\n")) {
61861
+ if (fs22.existsSync(indexFile)) {
61862
+ for (const line of fs22.readFileSync(indexFile, "utf8").split("\n")) {
60537
61863
  if (!line.trim()) continue;
60538
61864
  try {
60539
61865
  const row2 = JSON.parse(line);
@@ -60578,17 +61904,17 @@ async function archiveRunsCore(ctx, o) {
60578
61904
  const say = /* @__PURE__ */ __name((line) => {
60579
61905
  if (!ctx.json) console.log(line);
60580
61906
  }, "say");
60581
- const sha256 = /* @__PURE__ */ __name((buf) => crypto5.createHash("sha256").update(buf).digest("hex"), "sha256");
61907
+ const sha256 = /* @__PURE__ */ __name((buf) => crypto6.createHash("sha256").update(buf).digest("hex"), "sha256");
60582
61908
  const sleep4 = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "sleep");
60583
61909
  const archiveOne = /* @__PURE__ */ __name(async (apiRow) => {
60584
61910
  const run = {
60585
61911
  ...apiRow,
60586
61912
  id: runIdOf(apiRow)
60587
61913
  };
60588
- const zipFile = path27.join(o.out, `${run.id}.zip`);
61914
+ const zipFile = path30.join(o.out, `${run.id}.zip`);
60589
61915
  const prior = indexed.get(run.id);
60590
61916
  const completedAt = run.completedAt ? Date.parse(run.completedAt) : NaN;
60591
- if (prior && run.exportedAt && Number.isFinite(completedAt) && Date.parse(run.exportedAt) >= completedAt && fs20.existsSync(zipFile) && sha256(fs20.readFileSync(zipFile)) === prior.sha256) {
61917
+ if (prior && run.exportedAt && Number.isFinite(completedAt) && Date.parse(run.exportedAt) >= completedAt && fs22.existsSync(zipFile) && sha256(fs22.readFileSync(zipFile)) === prior.sha256) {
60592
61918
  report2.skipped.push(run.id);
60593
61919
  say(` ${run.id} \xB7 skipped (archived ${prior.exportId}, sha256 matches)`);
60594
61920
  return;
@@ -60683,7 +62009,7 @@ async function archiveRunsCore(ctx, o) {
60683
62009
  say(` ${run.id} \xB7 failed (sha256 mismatch)`);
60684
62010
  return;
60685
62011
  }
60686
- fs20.writeFileSync(zipFile, bytes);
62012
+ fs22.writeFileSync(zipFile, bytes);
60687
62013
  const row2 = {
60688
62014
  runId: run.id,
60689
62015
  workflowId: run.workflowId,
@@ -60692,7 +62018,7 @@ async function archiveRunsCore(ctx, o) {
60692
62018
  sha256: digest,
60693
62019
  bytes: bytes.length
60694
62020
  };
60695
- fs20.appendFileSync(indexFile, `${JSON.stringify(row2)}
62021
+ fs22.appendFileSync(indexFile, `${JSON.stringify(row2)}
60696
62022
  `);
60697
62023
  indexed.set(run.id, row2);
60698
62024
  report2.archived.push(run.id);
@@ -61453,10 +62779,10 @@ function parseJudgePredicateFlag(raw) {
61453
62779
  } : {}
61454
62780
  };
61455
62781
  }
61456
- const [path27, op, ...rest] = raw.trim().split(/\s+/);
61457
- if (!path27 || !op || !GOAL_PREDICATE_OPS.includes(op)) throw new WorkflowLocalUsageError("usage", `--judge-predicate: expected "<path> <op> [value]" with op ${opList} (got "${raw}")`);
62782
+ const [path30, op, ...rest] = raw.trim().split(/\s+/);
62783
+ if (!path30 || !op || !GOAL_PREDICATE_OPS.includes(op)) throw new WorkflowLocalUsageError("usage", `--judge-predicate: expected "<path> <op> [value]" with op ${opList} (got "${raw}")`);
61458
62784
  if (rest.length === 0) return {
61459
- path: path27,
62785
+ path: path30,
61460
62786
  op
61461
62787
  };
61462
62788
  const text = rest.join(" ");
@@ -61466,7 +62792,7 @@ function parseJudgePredicateFlag(raw) {
61466
62792
  } catch {
61467
62793
  }
61468
62794
  return {
61469
- path: path27,
62795
+ path: path30,
61470
62796
  op,
61471
62797
  value: value3
61472
62798
  };
@@ -64049,8 +65375,12 @@ var TemplateApiService = class {
64049
65375
  body: JSON.stringify(data)
64050
65376
  });
64051
65377
  }
64052
- async listTemplates() {
64053
- return this._fetch("/marketplace/templates");
65378
+ /**
65379
+ * PRO-2201 — the catalog hides never-published templates; `includeUnpublished`
65380
+ * (the template picker only) also lists the caller's own-org unpublished ones.
65381
+ */
65382
+ async listTemplates(opts = {}) {
65383
+ return this._fetch(`/marketplace/templates${opts.includeUnpublished ? "?includeUnpublished=true" : ""}`);
64054
65384
  }
64055
65385
  async getTemplate(templateId) {
64056
65386
  return this._fetch(`/marketplace/templates/${templateId}`);
@@ -64251,10 +65581,12 @@ function missingVersionOrRethrow(error) {
64251
65581
  throw error;
64252
65582
  }
64253
65583
  __name(missingVersionOrRethrow, "missingVersionOrRethrow");
64254
- async function resolveTemplateId(templateApi, options, message) {
65584
+ async function resolveTemplateId(templateApi, options, message, includeUnpublished) {
64255
65585
  if (options.templateId) return options.templateId;
64256
65586
  writeProgress("\u{1F504} Loading your templates...");
64257
- const { templates } = await templateApi.listTemplates();
65587
+ const { templates } = await templateApi.listTemplates({
65588
+ includeUnpublished
65589
+ });
64258
65590
  if (!templates.length) {
64259
65591
  throw new Error("You haven't created any templates yet. Run `lua marketplace template create` first.");
64260
65592
  }
@@ -64476,7 +65808,7 @@ __name(templateCreateAction, "templateCreateAction");
64476
65808
  async function templateDraftAction(templateApi, options) {
64477
65809
  const config = readYamlConfig();
64478
65810
  if (!config) throw new Error("No lua.skill.yaml found in this directory.");
64479
- const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to draft for?");
65811
+ const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to draft for?", true);
64480
65812
  const sourceAgentVersion = options.sourceVersion ? Number.parseInt(options.sourceVersion, 10) : void 0;
64481
65813
  if (options.sourceVersion && (!Number.isFinite(sourceAgentVersion) || sourceAgentVersion <= 0)) {
64482
65814
  throw new Error(`Invalid --source-version "${options.sourceVersion}": must be a positive integer.`);
@@ -64595,7 +65927,7 @@ async function confirmTemplateSectionConsequences(templateApi, templateId, local
64595
65927
  }
64596
65928
  __name(confirmTemplateSectionConsequences, "confirmTemplateSectionConsequences");
64597
65929
  async function templatePublishAction(templateApi, config, options) {
64598
- const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to publish a version for?");
65930
+ const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to publish a version for?", true);
64599
65931
  const sourceAgentVersion = options.sourceVersion ? Number.parseInt(options.sourceVersion, 10) : void 0;
64600
65932
  if (options.sourceVersion && (!Number.isFinite(sourceAgentVersion) || sourceAgentVersion <= 0)) {
64601
65933
  throw new Error(`Invalid --source-version "${options.sourceVersion}": must be a positive integer.`);
@@ -64637,19 +65969,27 @@ async function templatePublishAction(templateApi, config, options) {
64637
65969
  selfAudit: templateSection.selfAudit
64638
65970
  } : {}
64639
65971
  });
65972
+ const personaWarning = personaWarningLine(version, templateId);
64640
65973
  if (options.json) {
64641
65974
  console.log(JSON.stringify(version, null, 2));
65975
+ if (personaWarning) console.warn(personaWarning);
64642
65976
  return;
64643
65977
  }
64644
65978
  writeSuccess(`\u2705 Published v${version.version}`);
64645
65979
  writeInfo(`Frozen from agent version v${version.sourceAgentVersion}`);
64646
65980
  writeInfo(autoApplyLine(version, options.skipAutoApply === true));
65981
+ if (personaWarning) writeInfo(personaWarning);
64647
65982
  console.log("\nManifest:");
64648
65983
  printManifestSummary(version.content);
64649
65984
  const envCount = version.envContract ? Object.keys(version.envContract).length : 0;
64650
65985
  console.log(` Env contract: ${envCount} var(s)`);
64651
65986
  }
64652
65987
  __name(templatePublishAction, "templatePublishAction");
65988
+ function personaWarningLine(version, templateId) {
65989
+ if (version.personaTemplate) return void 0;
65990
+ return `\u26A0\uFE0F No persona template on v${version.version}: installs will run on the stock placeholder persona, not the source agent's. Compose one with \`lua marketplace template draft --template-id ${templateId}\`, then publish again.`;
65991
+ }
65992
+ __name(personaWarningLine, "personaWarningLine");
64653
65993
  function autoApplyLine(version, skipped) {
64654
65994
  if (skipped) return "Auto-update: skipped (--skip-auto-apply)";
64655
65995
  if (version.autoApplyRunId) {
@@ -64661,7 +66001,7 @@ function autoApplyLine(version, skipped) {
64661
66001
  }
64662
66002
  __name(autoApplyLine, "autoApplyLine");
64663
66003
  async function templateViewAction(templateApi, options) {
64664
- const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to view?");
66004
+ const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to view?", true);
64665
66005
  if (options.version) {
64666
66006
  const versionNum = Number.parseInt(options.version, 10);
64667
66007
  if (!Number.isFinite(versionNum) || versionNum <= 0) {
@@ -64706,7 +66046,7 @@ ${"=".repeat(60)}
64706
66046
  }
64707
66047
  __name(templateViewAction, "templateViewAction");
64708
66048
  async function templateVersionsAction(templateApi, options) {
64709
- const templateId = await resolveTemplateId(templateApi, options, "Which template\u2019s versions would you like to see?");
66049
+ const templateId = await resolveTemplateId(templateApi, options, "Which template\u2019s versions would you like to see?", true);
64710
66050
  const versions = await templateApi.getVersions(templateId);
64711
66051
  if (options.json) {
64712
66052
  console.log(JSON.stringify(versions, null, 2));
@@ -64728,7 +66068,7 @@ __name(templateVersionsAction, "templateVersionsAction");
64728
66068
  async function templateInstallAction(templateApi, config, options) {
64729
66069
  const agentId = config.agent?.agentId;
64730
66070
  if (!agentId) throw new Error("Agent ID not found in configuration.");
64731
- const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to install?");
66071
+ const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to install?", false);
64732
66072
  const version = options.version ? Number.parseInt(options.version, 10) : void 0;
64733
66073
  const envValues = options.envVars ? parseKeyValuePairs(options.envVars) : void 0;
64734
66074
  if (!options.force) {
@@ -64812,7 +66152,7 @@ function resolveApplyTargets(options) {
64812
66152
  }
64813
66153
  __name(resolveApplyTargets, "resolveApplyTargets");
64814
66154
  async function templateApplyAction(templateApi, options) {
64815
- const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to apply?");
66155
+ const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to apply?", false);
64816
66156
  let targets = resolveApplyTargets(options);
64817
66157
  if (!targets && options.allInstalled) {
64818
66158
  writeProgress("\u{1F504} Loading install ledger...");
@@ -64899,7 +66239,7 @@ Apply run ${run.status} (${run.id})`);
64899
66239
  }
64900
66240
  __name(templateApplyAction, "templateApplyAction");
64901
66241
  async function templateHealthAction(templateApi, options) {
64902
- const templateId = await resolveTemplateId(templateApi, options, "Which template\u2019s fleet health would you like?");
66242
+ const templateId = await resolveTemplateId(templateApi, options, "Which template\u2019s fleet health would you like?", true);
64903
66243
  const windowDays = options.windowDays !== void 0 ? parseInt(options.windowDays, 10) : void 0;
64904
66244
  if (windowDays !== void 0 && (!Number.isFinite(windowDays) || windowDays < 1)) {
64905
66245
  throw new Error("--window-days must be a positive integer");
@@ -64970,7 +66310,7 @@ async function templateHealthAction(templateApi, options) {
64970
66310
  }
64971
66311
  __name(templateHealthAction, "templateHealthAction");
64972
66312
  async function templateStatusAction(templateApi, options) {
64973
- const templateId = await resolveTemplateId(templateApi, options, "Which template\u2019s fleet status would you like to see?");
66313
+ const templateId = await resolveTemplateId(templateApi, options, "Which template\u2019s fleet status would you like to see?", true);
64974
66314
  const installs = await templateApi.getAllInstalls(templateId);
64975
66315
  if (options.json) {
64976
66316
  console.log(JSON.stringify(installs, null, 2));
@@ -65038,7 +66378,7 @@ __name(templateInstalledAction, "templateInstalledAction");
65038
66378
  async function templateUninstallAction(templateApi, config, options) {
65039
66379
  const agentId = config.agent?.agentId;
65040
66380
  if (!agentId) throw new Error("Agent ID not found in configuration.");
65041
- const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to uninstall?");
66381
+ const templateId = await resolveTemplateId(templateApi, options, "Which template would you like to uninstall?", false);
65042
66382
  if (!options.force) {
65043
66383
  throw CliError.usage("This action requires --force to confirm", `Use --force to confirm uninstalling template ${templateId} from this agent`);
65044
66384
  }
@@ -71384,13 +72724,13 @@ function hasSox() {
71384
72724
  }
71385
72725
  __name(hasSox, "hasSox");
71386
72726
  function detectSoxInstaller() {
71387
- const os3 = platform3();
72727
+ const os4 = platform3();
71388
72728
  const has3 = /* @__PURE__ */ __name((bin) => spawnSync(bin, [
71389
72729
  "--version"
71390
72730
  ], {
71391
72731
  stdio: "ignore"
71392
72732
  }).status === 0, "has");
71393
- if (os3 === "darwin") {
72733
+ if (os4 === "darwin") {
71394
72734
  if (has3("brew")) return {
71395
72735
  manager: "Homebrew",
71396
72736
  argv: [
@@ -71401,7 +72741,7 @@ function detectSoxInstaller() {
71401
72741
  };
71402
72742
  return null;
71403
72743
  }
71404
- if (os3 === "linux") {
72744
+ if (os4 === "linux") {
71405
72745
  if (has3("apt-get")) return {
71406
72746
  manager: "apt",
71407
72747
  argv: [
@@ -71469,7 +72809,7 @@ function detectSoxInstaller() {
71469
72809
  };
71470
72810
  return null;
71471
72811
  }
71472
- if (os3 === "win32") {
72812
+ if (os4 === "win32") {
71473
72813
  if (has3("winget")) return {
71474
72814
  manager: "winget",
71475
72815
  argv: [
@@ -72246,7 +73586,7 @@ function findVoiceTestFiles(root) {
72246
73586
  ".lua",
72247
73587
  "coverage"
72248
73588
  ]);
72249
- const walk3 = /* @__PURE__ */ __name((dir) => {
73589
+ const walk4 = /* @__PURE__ */ __name((dir) => {
72250
73590
  let entries;
72251
73591
  try {
72252
73592
  entries = readdirSync4(dir);
@@ -72263,13 +73603,13 @@ function findVoiceTestFiles(root) {
72263
73603
  }
72264
73604
  if (s.isDirectory()) {
72265
73605
  if (skipDirs.has(entry)) continue;
72266
- walk3(full);
73606
+ walk4(full);
72267
73607
  } else if (s.isFile() && /\.voice\.test\.(t|j)sx?$/.test(entry)) {
72268
73608
  out.push(resolve6(full));
72269
73609
  }
72270
73610
  }
72271
73611
  }, "walk");
72272
- walk3(root);
73612
+ walk4(root);
72273
73613
  return out;
72274
73614
  }
72275
73615
  __name(findVoiceTestFiles, "findVoiceTestFiles");
@@ -72531,7 +73871,8 @@ async function versionListCommand(options = {}) {
72531
73871
  createdByEmail: v.createdByEmail,
72532
73872
  createdAt: v.createdAt,
72533
73873
  commitHash: v.commitHash,
72534
- sourceManifestVersion: v.sourceManifestVersion
73874
+ sourceManifestVersion: v.sourceManifestVersion,
73875
+ sourceDigest: v.sourceDigest
72535
73876
  }));
72536
73877
  console.log(JSON.stringify(summary, null, 2));
72537
73878
  } else if (versions.length === 0) {
@@ -73290,6 +74631,284 @@ async function pullCommand(options = {}) {
73290
74631
  }
73291
74632
  __name(pullCommand, "pullCommand");
73292
74633
 
74634
+ // src/commands/apps.ts
74635
+ init_dist();
74636
+ init_cli();
74637
+ init_write_info();
74638
+ init_package_root();
74639
+ init_cli_error();
74640
+ init_analytics();
74641
+ import fs21 from "fs";
74642
+ import path29 from "path";
74643
+ import { IndentationText, Node as Node22, Project as Project5, QuoteKind, SyntaxKind } from "ts-morph";
74644
+ init_command_utils();
74645
+ init_artifact_loader();
74646
+ init_types();
74647
+ var WEB_APP_TEMPLATE_DIR = "template-web-app";
74648
+ var PAGES_EXCLUDE_GLOB = "src/apps/*/web";
74649
+ var NAME_PLACEHOLDER = "__APP_NAME__";
74650
+ function appIdentifier(name) {
74651
+ return name.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
74652
+ }
74653
+ __name(appIdentifier, "appIdentifier");
74654
+ function copyTemplate(from, to, name, written) {
74655
+ fs21.mkdirSync(to, {
74656
+ recursive: true
74657
+ });
74658
+ for (const entry of fs21.readdirSync(from, {
74659
+ withFileTypes: true
74660
+ })) {
74661
+ if (entry.name === "node_modules" || entry.name === "dist") continue;
74662
+ const src = path29.join(from, entry.name);
74663
+ const dest = path29.join(to, entry.name);
74664
+ if (entry.isDirectory()) {
74665
+ copyTemplate(src, dest, name, written);
74666
+ } else {
74667
+ fs21.writeFileSync(dest, fs21.readFileSync(src, "utf8").split(NAME_PLACEHOLDER).join(name));
74668
+ written.push(dest);
74669
+ }
74670
+ }
74671
+ }
74672
+ __name(copyTemplate, "copyTemplate");
74673
+ function excludePagesFromTsconfig(projectRoot) {
74674
+ const file = path29.join(projectRoot, "tsconfig.json");
74675
+ if (!fs21.existsSync(file)) return "missing";
74676
+ let config;
74677
+ try {
74678
+ config = JSON.parse(fs21.readFileSync(file, "utf8"));
74679
+ } catch {
74680
+ return "unparsable";
74681
+ }
74682
+ const exclude = Array.isArray(config.exclude) ? config.exclude : [
74683
+ "node_modules"
74684
+ ];
74685
+ if (exclude.includes(PAGES_EXCLUDE_GLOB)) return "present";
74686
+ config.exclude = [
74687
+ ...exclude,
74688
+ PAGES_EXCLUDE_GLOB
74689
+ ];
74690
+ fs21.writeFileSync(file, JSON.stringify(config, null, 2) + "\n");
74691
+ return "added";
74692
+ }
74693
+ __name(excludePagesFromTsconfig, "excludePagesFromTsconfig");
74694
+ function registerWebAppOnAgent(indexFile, name) {
74695
+ if (!fs21.existsSync(indexFile)) return "missing-index";
74696
+ const project = new Project5({
74697
+ skipAddingFilesFromTsConfig: true,
74698
+ // The style of the agent template (and of most agent code): single quotes, two spaces.
74699
+ manipulationSettings: {
74700
+ quoteKind: QuoteKind.Single,
74701
+ indentationText: IndentationText.TwoSpaces
74702
+ }
74703
+ });
74704
+ const sf = project.addSourceFileAtPath(indexFile);
74705
+ const agentNew = sf.getDescendantsOfKind(SyntaxKind.NewExpression).find((n2) => n2.getExpression().getText() === "LuaAgent");
74706
+ const config = agentNew?.getArguments()[0];
74707
+ if (!config || !Node22.isObjectLiteralExpression(config)) return "no-agent";
74708
+ const prop = config.getProperty("webApps");
74709
+ const list = prop && Node22.isPropertyAssignment(prop) ? prop.getInitializer() : void 0;
74710
+ if (prop && !(list && Node22.isArrayLiteralExpression(list))) return "webapps-not-array";
74711
+ const binding = appIdentifier(name);
74712
+ const specifier = `./apps/${name}/app`;
74713
+ if (!sf.getImportDeclaration((d) => d.getModuleSpecifierValue() === specifier)) {
74714
+ const last = sf.getImportDeclarations().at(-1);
74715
+ sf.insertImportDeclaration(last ? last.getChildIndex() + 1 : 0, {
74716
+ defaultImport: binding,
74717
+ moduleSpecifier: specifier
74718
+ });
74719
+ }
74720
+ if (list && Node22.isArrayLiteralExpression(list)) {
74721
+ if (!list.getElements().some((e) => e.getText() === binding)) list.addElement(binding);
74722
+ } else {
74723
+ config.addPropertyAssignment({
74724
+ name: "webApps",
74725
+ initializer: `[${binding}]`
74726
+ });
74727
+ }
74728
+ sf.saveSync();
74729
+ return "registered";
74730
+ }
74731
+ __name(registerWebAppOnAgent, "registerWebAppOnAgent");
74732
+ function scaffoldWebApp(projectRoot, name, templateRoot = getPackageRoot()) {
74733
+ if (!WEB_APP_NAME_PATTERN.test(name)) {
74734
+ throw CliError.usage(`App name must match ${WEB_APP_NAME_PATTERN} (lower-case letters, digits and dashes, starting with a letter); got ${JSON.stringify(name)}.`);
74735
+ }
74736
+ if (!fs21.existsSync(path29.join(projectRoot, "lua.skill.yaml"))) {
74737
+ throw CliError.usage("No lua.skill.yaml here: run `lua apps new` in the root of an agent project (`lua init` makes one).");
74738
+ }
74739
+ const appDir = path29.join(projectRoot, "src", "apps", name);
74740
+ if (fs21.existsSync(appDir)) {
74741
+ throw CliError.usage(`${path29.relative(projectRoot, appDir)} already exists; pick another name or delete it first.`);
74742
+ }
74743
+ const files = [];
74744
+ copyTemplate(path29.join(templateRoot, WEB_APP_TEMPLATE_DIR), appDir, name, files);
74745
+ return {
74746
+ appDir,
74747
+ files,
74748
+ registered: registerWebAppOnAgent(path29.join(projectRoot, "src", "index.ts"), name),
74749
+ tsconfig: excludePagesFromTsconfig(projectRoot)
74750
+ };
74751
+ }
74752
+ __name(scaffoldWebApp, "scaffoldWebApp");
74753
+ async function appsNewCommand(name) {
74754
+ return withErrorHandling(async () => {
74755
+ const projectRoot = process.cwd();
74756
+ const result = scaffoldWebApp(projectRoot, name);
74757
+ const rel = /* @__PURE__ */ __name((p) => path29.relative(projectRoot, p), "rel");
74758
+ for (const file of result.files) writeInfo(` created ${rel(file)}`);
74759
+ const binding = appIdentifier(name);
74760
+ const addLines = ` import ${binding} from './apps/${name}/app';
74761
+ webApps: [${binding}], (on your LuaAgent)`;
74762
+ switch (result.registered) {
74763
+ case "registered":
74764
+ writeInfo(` updated src/index.ts webApps: [${binding}]`);
74765
+ break;
74766
+ case "missing-index":
74767
+ writeInfo(` \u26A0\uFE0F No src/index.ts. Where your LuaAgent is defined, add:
74768
+ ${addLines}`);
74769
+ break;
74770
+ case "no-agent":
74771
+ writeInfo(` \u26A0\uFE0F No \`new LuaAgent({...})\` in src/index.ts. Where your LuaAgent is defined, add:
74772
+ ${addLines}`);
74773
+ break;
74774
+ case "webapps-not-array":
74775
+ writeInfo(` \u26A0\uFE0F \`webApps\` in src/index.ts is not an array literal, so it was left as is. Add ${binding} to it and:
74776
+ import ${binding} from './apps/${name}/app';`);
74777
+ break;
74778
+ }
74779
+ if (result.tsconfig === "added") writeInfo(` updated tsconfig.json exclude: "${PAGES_EXCLUDE_GLOB}"`);
74780
+ if (result.tsconfig === "unparsable" || result.tsconfig === "missing") {
74781
+ writeInfo(` \u26A0\uFE0F Add "${PAGES_EXCLUDE_GLOB}" to "exclude" in tsconfig.json so the agent compiler never loads the pages.`);
74782
+ }
74783
+ writeSuccess(`
74784
+ \u2705 Web app ${name} created`);
74785
+ writeHintBlock({
74786
+ headline: "Next:",
74787
+ lines: [
74788
+ {
74789
+ label: "Install pages:",
74790
+ command: `cd ${rel(path29.join(result.appDir, "web"))} && npm install`
74791
+ },
74792
+ {
74793
+ label: "Run locally:",
74794
+ command: `lua apps dev ${name}`
74795
+ },
74796
+ {
74797
+ label: "Ship:",
74798
+ command: `lua push webapp --name ${name}`
74799
+ }
74800
+ ],
74801
+ when: "success"
74802
+ });
74803
+ trackEvent("cli_apps_new", {
74804
+ registered: result.registered,
74805
+ tsconfig: result.tsconfig
74806
+ });
74807
+ }, "apps new");
74808
+ }
74809
+ __name(appsNewCommand, "appsNewCommand");
74810
+ async function appsDevCommand(name, opts = {}) {
74811
+ return withErrorHandling(async () => {
74812
+ await compileCommand();
74813
+ const { apiKey, agentId, config } = await initializeCommand({
74814
+ showProgress: false
74815
+ });
74816
+ const find = /* @__PURE__ */ __name(() => getPrimitivesByKind(loadManifest(), PrimitiveKind.WEB_APP).find((a) => a.name === name), "find");
74817
+ const app = find();
74818
+ if (!app) {
74819
+ throw CliError.usage(`No web app named ${JSON.stringify(name)} in the compiled agent.`, `Create one with: lua apps new ${name} (and list it under \`webApps\` on your LuaAgent)`);
74820
+ }
74821
+ const appDir = path29.dirname(app.sourcePath);
74822
+ const pagesDir = path29.resolve(appDir, app.pagesRoot);
74823
+ const vite = await loadAppVite(pagesDir);
74824
+ if (!vite.createServer) throw new WebAppBuildError(`The Vite in ${pagesDir} exposes no createServer().`);
74825
+ const auth = await developerAuth(apiKey, agentId, config);
74826
+ const addedEnv = [];
74827
+ for (const [key, value3] of Object.entries(loadEnvironmentVariables2())) {
74828
+ if (process.env[key] === void 0) {
74829
+ process.env[key] = value3;
74830
+ addedEnv.push(key);
74831
+ }
74832
+ }
74833
+ let code = loadArtifact(app, process.cwd());
74834
+ let current = app;
74835
+ const ctx = /* @__PURE__ */ __name(() => ({
74836
+ app: current,
74837
+ appCode: /* @__PURE__ */ __name(() => code, "appCode"),
74838
+ auth,
74839
+ apiKey,
74840
+ agentId
74841
+ }), "ctx");
74842
+ let origin = "";
74843
+ const server = await vite.createServer({
74844
+ root: pagesDir,
74845
+ server: {
74846
+ port: opts.port ? Number(opts.port) : void 0
74847
+ },
74848
+ plugins: [
74849
+ luaAppsDevPlugin({
74850
+ context: ctx,
74851
+ origin: /* @__PURE__ */ __name(() => origin, "origin"),
74852
+ appDir,
74853
+ pagesDir,
74854
+ log: /* @__PURE__ */ __name((line, status) => (status >= 500 ? console.error : console.log)(` ${line}`), "log"),
74855
+ onRouteChange: /* @__PURE__ */ __name(async (file) => {
74856
+ writeInfo(`
74857
+ \u267B\uFE0F ${path29.relative(process.cwd(), file)} changed \u2014 recompiling routes...`);
74858
+ try {
74859
+ await compileCommand();
74860
+ const next = find();
74861
+ if (!next) throw new Error(`web app ${name} is gone from the compiled agent`);
74862
+ current = next;
74863
+ code = loadArtifact(next, process.cwd());
74864
+ writeSuccess("\u2705 Routes reloaded");
74865
+ } catch (error) {
74866
+ console.error(`\u274C Recompile failed; still serving the previous routes: ${error?.message}`);
74867
+ }
74868
+ }, "onRouteChange")
74869
+ })
74870
+ ]
74871
+ });
74872
+ await server.listen();
74873
+ origin = server.resolvedUrls?.local?.[0]?.replace(/\/$/, "") ?? "";
74874
+ writeSuccess(`
74875
+ \u2705 ${name} is running`);
74876
+ writeHintBlock({
74877
+ headline: "Lua apps dev:",
74878
+ lines: [
74879
+ {
74880
+ label: "Pages:",
74881
+ command: `${origin || "http://localhost"} (Vite, hot reload)`
74882
+ },
74883
+ {
74884
+ label: "Routes:",
74885
+ command: "/_lua/api/* \u2192 local sandbox, auth = you (Data and env() are live)"
74886
+ },
74887
+ {
74888
+ label: "Stop:",
74889
+ command: "Ctrl+C"
74890
+ }
74891
+ ],
74892
+ when: "success"
74893
+ });
74894
+ trackEvent("cli_apps_dev", {
74895
+ routes: app.routes.length,
74896
+ hasFetch: app.hasFetch
74897
+ });
74898
+ await new Promise((resolve7) => {
74899
+ const stop = /* @__PURE__ */ __name(() => {
74900
+ process.off("SIGINT", stop);
74901
+ process.off("SIGTERM", stop);
74902
+ for (const key of addedEnv) delete process.env[key];
74903
+ void server.close().finally(resolve7);
74904
+ }, "stop");
74905
+ process.once("SIGINT", stop);
74906
+ process.once("SIGTERM", stop);
74907
+ });
74908
+ }, "apps dev");
74909
+ }
74910
+ __name(appsDevCommand, "appsDevCommand");
74911
+
73293
74912
  // src/cli/command-definitions.ts
73294
74913
  init_cli();
73295
74914
  function collect(value3, previous) {
@@ -73465,7 +75084,7 @@ Examples:
73465
75084
  ...opts,
73466
75085
  accept: opts.pull || opts.accept
73467
75086
  }));
73468
- program2.command("test [type]").description("\u{1F9EA} Test skills, webhooks, or jobs interactively").option("--name <name>", "Entity name to test").option("--input <json>", "JSON input string for testing").option("--json", "Output result as raw JSON (useful for scripting and piping)").option("--step-output <id=json>", "Workflow: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "Workflow: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "Workflow: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "Workflow: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "Workflow: seed completed steps from a real run").option("--force", "Workflow: seed from a run whose graph differs").option("--record <dir>", "Workflow: record agent/tool outputs as fixtures").option("--fixtures <dir>", "Workflow: replay recorded fixtures (exit 5 when one is missing)").option("--step-wall <s>", "Workflow: per-step wall in seconds (default 600)").option("--job-wall <s>", "Workflow: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--max-ticks <n>", "Workflow (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--ledger-out <file>", "Workflow: write the in-memory ledger as JSON").option("--agents <fake|live>", "Workflow: fake agent steps (default) or call the dev API").option("--now <iso>", "Workflow: virtual clock start").option("--park <id>", "Workflow: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "Workflow: collapse retry backoff waits to 0").option("--real-time", "Workflow: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "Workflow: back ctx.artefacts.* on disk").option("--workspace <dir>", "Workflow: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").addHelpText("after", `
75087
+ program2.command("test [type]").description("\u{1F9EA} Test skills, webhooks, or jobs interactively").option("--name <name>", "Entity name to test").option("--input <json>", "JSON input string for testing").option("--json", "Output result as raw JSON (useful for scripting and piping)").option("--route <route>", "Web app: the route to call, e.g. 'GET /tickets?status=open'").option("--step-output <id=json>", "Workflow: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "Workflow: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "Workflow: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "Workflow: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "Workflow: seed completed steps from a real run").option("--force", "Workflow: seed from a run whose graph differs").option("--record <dir>", "Workflow: record agent/tool outputs as fixtures").option("--fixtures <dir>", "Workflow: replay recorded fixtures (exit 5 when one is missing)").option("--step-wall <s>", "Workflow: per-step wall in seconds (default 600)").option("--job-wall <s>", "Workflow: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--max-ticks <n>", "Workflow (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--ledger-out <file>", "Workflow: write the in-memory ledger as JSON").option("--agents <fake|live>", "Workflow: fake agent steps (default) or call the dev API").option("--now <iso>", "Workflow: virtual clock start").option("--park <id>", "Workflow: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "Workflow: collapse retry backoff waits to 0").option("--real-time", "Workflow: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "Workflow: back ctx.artefacts.* on disk").option("--workspace <dir>", "Workflow: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").addHelpText("after", `
73469
75088
  Arguments:
73470
75089
  type Optional: 'skill', 'webhook', 'job' or 'workflow' (prompts if not provided)
73471
75090
 
@@ -73487,7 +75106,7 @@ Examples:
73487
75106
  `).action(testCommand);
73488
75107
  program2.command("push [type]").description("\u2601\uFE0F Push skill, agent, webhook, job, MCP server, or backup to server").option("--force", "Skip all confirmation prompts (auto-confirm)").option("--auto-deploy", "Automatically deploy to production after push").option("--name <name>", "Entity name to push").option("--set-version <version>", "Version to set (e.g., 1.0.5)").option("--fresh", "Build backup manifest from disk instead of compiled manifest.json (push backup only)").option("--apply-effort", "push workflow: send each agent step's effort to the model from this version on (a plain push never does)").option("--no-include-source", "Skip the per-skill source attach after push (default: attach, so admin Builder UI sees CLI edits)").addHelpText("after", `
73489
75108
  Arguments:
73490
- type Optional: 'skill', 'agent', 'persona', 'webhook', 'job', 'preprocessor', 'postprocessor', 'mcp', 'backup', or 'all' (prompts if not provided)
75109
+ type Optional: 'skill', 'agent', 'persona', 'webhook', 'job', 'preprocessor', 'postprocessor', 'mcp', 'webapp', 'backup', or 'all' (prompts if not provided)
73491
75110
 
73492
75111
  Options:
73493
75112
  --force Skip all confirmation prompts
@@ -73505,6 +75124,7 @@ Examples:
73505
75124
  $ lua push webhook Push a webhook directly
73506
75125
  $ lua push job Push a job directly
73507
75126
  $ lua push mcp Push an MCP server directly
75127
+ $ lua push webapp --name ops-dashboard Build and push a web app (routes + pages)
73508
75128
  $ lua push backup Push project source backup
73509
75129
  $ lua push backup --fresh Push backup, building manifest from disk
73510
75130
  $ lua push all --force Push all components without prompts
@@ -74196,6 +75816,9 @@ Examples:
74196
75816
  $ lua version delete v3 --ci Refused: needs --force (usage error, exit 2)
74197
75817
  $ lua version delete v3 --ci --force Delete non-interactively
74198
75818
  `).action((version, opts) => versionDeleteCommand(version, opts));
75819
+ const appsGroup = program2.command("apps").description("\u{1F5A5}\uFE0F Create and develop web apps of this agent");
75820
+ appsGroup.command("new <name>").description("Scaffold a web app in src/apps/<name> (routes + a Vite/React page) and list it on the agent").action((name) => appsNewCommand(name));
75821
+ appsGroup.command("dev <name>").description("Run a web app locally: pages on Vite with hot reload, routes in the local sandbox as you").option("--port <port>", "Port for the dev server (default: Vite's)").action((name, opts) => appsDevCommand(name, opts));
74199
75822
  const gitGroup = program2.command("git").description("Manage opt-in git auto-commits for this project");
74200
75823
  gitGroup.command("connect").description("Enable git auto-commits (runs sanity checks, writes git.enabled: true)").option("--auto-push", "Also enable post-commit auto-push (requires `lua git auth github` + a GitHub HTTPS origin remote)").action((opts) => gitConnectCommand({
74201
75824
  autoPush: opts.autoPush