booking-agent-cli 0.10.0__tar.gz → 0.10.2__tar.gz
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.
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/PKG-INFO +2 -2
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/README.md +1 -1
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/pyproject.toml +1 -1
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/__init__.py +1 -1
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/__init__.py +1 -1
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/bundled_skill.md +20 -22
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/calendar_preference.py +16 -4
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/flow.py +69 -35
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/runtime_prepare.py +84 -1
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_cli_enforced_gates.py +50 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_resume_after_restart.py +158 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/.gitignore +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/authorization.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/backend/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/backend/live.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/filter.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/google.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/oauth_listener.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/scope.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/circuit_breaker.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/config.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/defaults.env +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/discovery.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/faults.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/headless.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/http_deadlines.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/http_retry.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/http_trace.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/merchant_pacing.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/models/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/models/platform.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/models/wire.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/namespace.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/observability.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/platform_profile.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/pop.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/profile_cache.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/registry_url.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/rest_client.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/secure_file.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/settings.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/signals.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/step_timing.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_capabilities.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_checkout.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_negotiation.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_profile.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent/usp_methods.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/activity_log.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/booking_poll.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/booking_summary.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/calendar_connection.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/calendar_event.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/catalog.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/checkout_prereq.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/config.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/lowlevel.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/runtime.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/update.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/config_loader.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/engine.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/fault_report_schema.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/fault_reports.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/flow_timings.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/gates.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/geo_eligibility.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/geocode.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/identity.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/install_integrity.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/link_cli.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/main.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/output.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/payment_helpers.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/payment_token.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/provenance.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/reschedule.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/runtime.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/search_area.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/session.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/skill_bundle.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/skill_integrity.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/support_bundle_schema.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/support_report.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/timezone_context.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/updates.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/.env.tests +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/conftest.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/test_cancel_reschedule_e2e.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/test_latency_soak.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/test_staging_registry_integration.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/test_timezone_booking_e2e.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/test_ucp_native_e2e.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/test_ucp_skill_flow_e2e.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/e2e/ucp_native.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_activity_log.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_at_rest_hygiene.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_booking_rejection.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_bulk_cancel.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_connection.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_connector.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_flow.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_preference.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_checkout_signals.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_circuit_breaker.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_config.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_correlation_flow.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_declined_spt.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_discover_registry_rejection.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_flow_evidence.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_flow_gates.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_flow_reschedule.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_flow_step_timings.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_google_calendar_oauth.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_home_visit_address.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_http_trace.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_identity_logout.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_install_integrity.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_lazy_oauth.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_link_auth.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_link_cli_spt.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_namespace.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_oauth_listener.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_oauth_metadata.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_platform_key_pop.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_platform_profile.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_pop_keystore.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_profile_cache.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_regression_index.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_release_hold_uses_delete.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_request_retry_semantics.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_runtime_prepare.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_search_area.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_session_compat.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_session_retention.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_skill_integrity.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_skill_portability.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_support_report.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_timezone_context.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_cancel_result.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_checkout_pop.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_checkout_proof_wireup.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_checkout_validate.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_profile.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_update_command.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_wheel_contents.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/SOURCE +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/acp_booking_extension.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/availability.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/booking.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/calendar_freebusy.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/catalog.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/paid_bookings.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/profile.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/registry.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/rest_common.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/usp.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/waitlist.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/spec_schemas/webhook_event.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/fixtures/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/fixtures/profiles.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/fixtures/provenance/pypi_attestations-0.0.30-py3-none-any.whl.provenance.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/fixtures/services.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/backend.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/link_cli.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/mock_calendar.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/pop_business.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/pop_transport.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/ucp_checkout_store.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/mock/ucp_server.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/offline_stack/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/support/offline_stack/stack.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/__init__.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_authorization_policy.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_booking_poll_credentials.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_booking_request_error.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_calendar_filter.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_catalog.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_check_version.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_conditional_writes.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_delivery_address.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_discovery.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_fault_reports.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_geo_eligibility.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_geocode.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_headless.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_http_deadlines.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_http_retry.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_live_search_registry.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_merchant_pacing.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_mock_support.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_money_path_events.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_nonce_from_pop_error_body.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_observability.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_outbound_member_names.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_package_layering.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_pay_at_service.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_provenance.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_registry_search_filters.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_registry_search_pricing.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_registry_search_responses.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_registry_url.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_release_notes.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_rest_client.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_scoped_method_authorization.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_session_credentials.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_spec_conformance.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_step_timing.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_capabilities.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_complete_outcome.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_create_price_guard.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_negotiation.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_updates.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/unit/test_wire_model_conformance.py +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/001-jcs-nested-buyer.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/002-jcs-meta-only.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/003-jcs-unicode-empty-meta.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/004-proof-issuance-no-ath.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/005-proof-presentation-ath-nonce.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/006-cross-key-replay.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/007-downgrade-cnf-without-proof.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/008-replayed-jti.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/009-aud-mismatch.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/010-alg-none.json +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/README.md +0 -0
- {booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/vectors/pop/SOURCE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: booking-agent-cli
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
4
4
|
Summary: The Booking Agent CLI: a stateful USP booking flow with hard gates, plus its USP protocol library.
|
|
5
5
|
Project-URL: Homepage, https://github.com/wix/booking-agent
|
|
6
6
|
Project-URL: Documentation, https://github.com/wix/booking-agent/blob/master/booking-agent/README.md
|
|
@@ -78,7 +78,7 @@ Each release also publishes that skill at an immutable `skill-v<version>` tag an
|
|
|
78
78
|
the release workflow, so a harness can pin and verify the exact text it follows:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.57.
|
|
81
|
+
curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.57.4/booking-agent/skills/booking-agent/SKILL.md
|
|
82
82
|
gh attestation verify SKILL.md --repo wix/booking-agent \
|
|
83
83
|
--signer-workflow wix/booking-agent/.github/workflows/release.yml
|
|
84
84
|
```
|
|
@@ -36,7 +36,7 @@ Each release also publishes that skill at an immutable `skill-v<version>` tag an
|
|
|
36
36
|
the release workflow, so a harness can pin and verify the exact text it follows:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.57.
|
|
39
|
+
curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.57.4/booking-agent/skills/booking-agent/SKILL.md
|
|
40
40
|
gh attestation verify SKILL.md --repo wix/booking-agent \
|
|
41
41
|
--signer-workflow wix/booking-agent/.github/workflows/release.yml
|
|
42
42
|
```
|
{booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/bundled_skill.md
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
version: 0.57.
|
|
2
|
+
version: 0.57.4
|
|
3
3
|
name: booking-agent
|
|
4
4
|
description: |
|
|
5
5
|
Books time-based services via USP from a Link wallet. Use when the user wants to book, schedule, reserve, or pay for a service from a business (haircut, lesson, repair, class, appointment, consult). The `booking-agent` CLI owns the booking state machine and refuses to proceed when human gates are skipped. Sign the user in to Link on your own, right after install and at the start of every booking (`booking-agent flow link-auth connect`, then start `booking-agent flow link-auth poll` in the background and post the sign-in URL in the same turn, exactly like `pay poll`); never wait for the user to ask to sign in or to say they approved. After `booking-agent flow business use`, follow `next_command` through `use-ucp-checkout-mode`; businesses without native checkout fail closed. Payment uses `booking-agent flow pay request` (default returns immediately with the approval URL), then start `booking-agent flow pay poll` in the background and post the approval URL to the user in the same turn; the poll finishes checkout once they approve. Confirm through `booking-agent flow book poll --wait`; never start an inbound webhook. Surface the approval URL exactly once. Personal identification (name, phone, email, address) must always come from the user's Link account via `link-cli`, never from chat or placeholders. Before any location-based search, the CLI proposes the Link shipping address and the user confirms it or gives another; online services skip this. Talk to the user in plain everyday language only; never expose CLI, UCP, auth, session, or other implementation details unless they explicitly ask.
|
|
@@ -19,7 +19,7 @@ metadata:
|
|
|
19
19
|
- link-cli
|
|
20
20
|
install:
|
|
21
21
|
- kind: uv
|
|
22
|
-
package: "booking-agent-cli==0.10.
|
|
22
|
+
package: "booking-agent-cli==0.10.2"
|
|
23
23
|
bins: [booking-agent]
|
|
24
24
|
user-invocable: true
|
|
25
25
|
---
|
|
@@ -220,7 +220,7 @@ To take a fixed, published version instead of the latest, fetch it at the releas
|
|
|
220
220
|
workflow signs it under, and check the signature before you follow it:
|
|
221
221
|
|
|
222
222
|
```bash
|
|
223
|
-
curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.57.
|
|
223
|
+
curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.57.4/booking-agent/skills/booking-agent/SKILL.md
|
|
224
224
|
gh attestation verify SKILL.md \
|
|
225
225
|
--repo wix/booking-agent \
|
|
226
226
|
--signer-workflow wix/booking-agent/.github/workflows/release.yml
|
|
@@ -245,20 +245,20 @@ before installing:
|
|
|
245
245
|
|
|
246
246
|
```bash
|
|
247
247
|
dir="$HOME/.booking-agent/verified-wheels"
|
|
248
|
-
uvx pip download "booking-agent-cli==0.10.
|
|
248
|
+
uvx pip download "booking-agent-cli==0.10.2" --no-deps --only-binary :all: -d "$dir"
|
|
249
249
|
uvx pypi-attestations==0.0.30 verify pypi --repository https://github.com/wix/booking-agent \
|
|
250
|
-
"$dir/booking_agent_cli-0.10.
|
|
251
|
-
uv tool install --force "$dir/booking_agent_cli-0.10.
|
|
250
|
+
"$dir/booking_agent_cli-0.10.2-py3-none-any.whl"
|
|
251
|
+
uv tool install --force "$dir/booking_agent_cli-0.10.2-py3-none-any.whl"
|
|
252
252
|
|
|
253
253
|
booking-agent --help
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
-
Install only
|
|
257
|
-
available, install the same pinned release with `uv tool install booking-agent-cli==0.10.
|
|
258
|
-
`pipx install booking-agent-cli==0.10.
|
|
256
|
+
Install only a wheel whose verify step passed. If `uvx` is not
|
|
257
|
+
available, install the same pinned release with `uv tool install booking-agent-cli==0.10.2` or
|
|
258
|
+
`pipx install booking-agent-cli==0.10.2`; the CLI checks its own files against the signed release
|
|
259
259
|
on the first `booking-agent flow init` and refuses to book or pay if they do not match.
|
|
260
260
|
|
|
261
|
-
Harnesses that read
|
|
261
|
+
Harnesses that read this skill's `install` block do this for you. Do not go on
|
|
262
262
|
until `booking-agent --help` runs. If it still does not, run the commands above yourself; if that
|
|
263
263
|
is not possible in this runtime, ask the user to install the skill, then try again. To develop the
|
|
264
264
|
CLI from a checkout instead, see the [Booking Agent README](https://github.com/wix/booking-agent/blob/master/booking-agent/README.md).
|
|
@@ -281,13 +281,13 @@ folder that survives between conversations, re-run the same init with `--persist
|
|
|
281
281
|
path of that folder>`: the CLI links saved bookings and the Link sign-in into it, merging what each
|
|
282
282
|
folder holds (booking files by session, the newer copy of anything else) and deleting nothing. If
|
|
283
283
|
the runtime keeps its files anyway, or gives no such folder, re-run it with `--no-persistent-root`.
|
|
284
|
-
If it answers `persistence_folder_not_durable`
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
`persistence_setup_failed` and `persistence_flags_conflict` say what to change. Use the same folder every time, never guess a path, never ask the user, and never mention it. A host
|
|
284
|
+
If it answers `persistence_folder_not_durable` (temporary, memory-backed, or unwritable), name the
|
|
285
|
+
folder the runtime really keeps, or use `--no-persistent-root`.
|
|
286
|
+
`persistence_setup_failed` and `persistence_flags_conflict` say what to change. Use the same folder every time; never guess a path, ask the user, or mention it. A host
|
|
288
287
|
that sets `BOOKING_AGENT_PERSISTENT_ROOT` or `BOOKING_AGENT_PERSISTENCE=none` is never asked. If the
|
|
289
288
|
user is asked to sign in or reconnect right after a restart, run `booking-agent runtime doctor` and
|
|
290
|
-
follow its `next_command`.
|
|
289
|
+
follow its `next_command`. If `flow status` or `flow init` answers `saved_state_not_linked`, run its `next_command` once, then
|
|
290
|
+
`flow status`; never answer it with `--no-persistent-root` or a new booking. For extra cache folders the runtime asks you to keep, run `booking-agent
|
|
291
291
|
runtime prepare --persistent-root <folder> --link <runtime folder>=<persistent folder>`.
|
|
292
292
|
|
|
293
293
|
**Sign the user in to Link**, as described in [Link sign-in](#link-sign-in-automatic). It works
|
|
@@ -296,9 +296,8 @@ with no booking in progress. Do not wait for the user to offer to sign in.
|
|
|
296
296
|
**Pass `--skill-path` and `--skill-version` on every `flow init`**: the path you loaded this file
|
|
297
297
|
from, and the version from the frontmatter above.
|
|
298
298
|
|
|
299
|
-
Nothing needs configuring
|
|
300
|
-
|
|
301
|
-
reach Google Calendar (see [the calendar gate](#step-3-select-business-and-resolve-calendar-gate)).
|
|
299
|
+
Nothing needs configuring: every setting has a shipped default, and the user never configures the
|
|
300
|
+
calendar: you work out at runtime how this runtime can reach Google Calendar (see [the calendar gate](#step-3-select-business-and-resolve-calendar-gate)).
|
|
302
301
|
**Never** write tokens, keys, or personal details into any env file. `booking-agent config show`
|
|
303
302
|
prints each effective setting and where it came from; `booking-agent config init` creates
|
|
304
303
|
`~/.booking-agent/.env` for an operator to edit.
|
|
@@ -306,9 +305,8 @@ prints each effective setting and where it came from; `booking-agent config init
|
|
|
306
305
|
### Updates (only with the user's yes)
|
|
307
306
|
|
|
308
307
|
The CLI decides whether a newer version is worth mentioning; you never decide from version
|
|
309
|
-
numbers or release pages.
|
|
310
|
-
|
|
311
|
-
installed CLI no longer match.
|
|
308
|
+
numbers or release pages. The `--skill-version` you pass on every `flow init` lets the CLI tell when these instructions
|
|
309
|
+
and the installed CLI no longer match.
|
|
312
310
|
|
|
313
311
|
- When a command returns `awaiting_user: update_consent`, ask the question in `prompt` in plain
|
|
314
312
|
words, using `data.user_summary` as the reason, and **STOP**. On yes, run `next_command`
|
|
@@ -470,7 +468,7 @@ confirmation email or on the business's page (`booking_url`). Do not invent a lo
|
|
|
470
468
|
**Step 1b — Create a new session:**
|
|
471
469
|
|
|
472
470
|
```bash
|
|
473
|
-
booking-agent flow init --user-request "<user's exact request>" --skill-version 0.57.
|
|
471
|
+
booking-agent flow init --user-request "<user's exact request>" --skill-version 0.57.4 \
|
|
474
472
|
--skill-path "<absolute path of the SKILL.md you loaded>"
|
|
475
473
|
```
|
|
476
474
|
|
{booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/calendar_preference.py
RENAMED
|
@@ -85,6 +85,18 @@ def _pref_applicable(
|
|
|
85
85
|
return False
|
|
86
86
|
|
|
87
87
|
|
|
88
|
+
_AT_OR_BEFORE_CALENDAR_GATE = frozenset({"INIT", "PREREQ_OK", "BUSINESS_SELECTED", "CALENDAR_GATE"})
|
|
89
|
+
# Phases where the user has already chosen a time: a calendar reconnect then changes nothing
|
|
90
|
+
# about the booking, and must never send it back to choose a time again.
|
|
91
|
+
CALENDAR_RECONNECTED_LATE_PHASES = frozenset({"SLOT_SELECTED", "BUYER_RESOLVED"})
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _reach_calendar_gate(session: Session) -> None:
|
|
95
|
+
"""Move to the calendar gate only from before it; a later phase keeps its place."""
|
|
96
|
+
if session.flow_phase in _AT_OR_BEFORE_CALENDAR_GATE:
|
|
97
|
+
transition(session, to="CALENDAR_GATE")
|
|
98
|
+
|
|
99
|
+
|
|
88
100
|
def try_apply_remembered_calendar_gate(
|
|
89
101
|
session: Session,
|
|
90
102
|
connection: Dict[str, Any],
|
|
@@ -101,7 +113,7 @@ def try_apply_remembered_calendar_gate(
|
|
|
101
113
|
session.calendar_remembered_applied = True
|
|
102
114
|
session.calendar_state = "skipped"
|
|
103
115
|
session.calendar_mode = CALENDAR_MODE_NONE
|
|
104
|
-
|
|
116
|
+
_reach_calendar_gate(session)
|
|
105
117
|
save_session(session)
|
|
106
118
|
return RememberedCalendarResult(
|
|
107
119
|
applied=True,
|
|
@@ -118,7 +130,7 @@ def try_apply_remembered_calendar_gate(
|
|
|
118
130
|
session.calendar_remembered_applied = True
|
|
119
131
|
session.calendar_state = "connected"
|
|
120
132
|
session.calendar_mode = mode
|
|
121
|
-
|
|
133
|
+
_reach_calendar_gate(session)
|
|
122
134
|
save_session(session)
|
|
123
135
|
if mode == CALENDAR_MODE_EVENTS:
|
|
124
136
|
prompt = (
|
|
@@ -148,7 +160,7 @@ def complete_calendar_gate_with_preference(
|
|
|
148
160
|
session.calendar_gate_completed = True
|
|
149
161
|
session.calendar_state = state
|
|
150
162
|
session.calendar_mode = mode
|
|
151
|
-
|
|
163
|
+
_reach_calendar_gate(session)
|
|
152
164
|
save_session(session)
|
|
153
165
|
save_calendar_preference(state=state, mode=mode)
|
|
154
166
|
|
|
@@ -163,5 +175,5 @@ def skip_calendar_gate_session_only(session: Session, *, unasked: bool = False)
|
|
|
163
175
|
session.calendar_state = "skipped"
|
|
164
176
|
session.calendar_mode = CALENDAR_MODE_NONE
|
|
165
177
|
session.calendar_skipped_unasked = unasked
|
|
166
|
-
|
|
178
|
+
_reach_calendar_gate(session)
|
|
167
179
|
save_session(session)
|
{booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/flow.py
RENAMED
|
@@ -726,6 +726,19 @@ def _resolve_persistence(persistent_root: Optional[str], no_persistent_root: boo
|
|
|
726
726
|
)
|
|
727
727
|
raise typer.Exit(code=1)
|
|
728
728
|
return
|
|
729
|
+
if persistence_resolution() in (None, "acknowledged"):
|
|
730
|
+
from ..runtime_prepare import find_unlinked_state, saved_state_not_linked_message
|
|
731
|
+
|
|
732
|
+
unlinked = find_unlinked_state()
|
|
733
|
+
if unlinked is not None:
|
|
734
|
+
emit_error(
|
|
735
|
+
"INIT",
|
|
736
|
+
code="saved_state_not_linked",
|
|
737
|
+
message=saved_state_not_linked_message(unlinked),
|
|
738
|
+
next_command=f"booking-agent runtime prepare --persistent-root {unlinked}",
|
|
739
|
+
data={"saved_state_folder": str(unlinked)},
|
|
740
|
+
)
|
|
741
|
+
raise typer.Exit(code=1)
|
|
729
742
|
if no_persistent_root:
|
|
730
743
|
acknowledge_no_persistence()
|
|
731
744
|
return
|
|
@@ -768,6 +781,18 @@ def _unfinished_similar_sessions(store: Any, user_request: str) -> List[Dict[str
|
|
|
768
781
|
def status() -> None:
|
|
769
782
|
session = load_session()
|
|
770
783
|
if session is None:
|
|
784
|
+
from ..runtime_prepare import find_unlinked_state, saved_state_not_linked_message
|
|
785
|
+
|
|
786
|
+
unlinked = find_unlinked_state()
|
|
787
|
+
if unlinked is not None:
|
|
788
|
+
emit_error(
|
|
789
|
+
"INIT",
|
|
790
|
+
code="saved_state_not_linked",
|
|
791
|
+
message=saved_state_not_linked_message(unlinked),
|
|
792
|
+
next_command=f"booking-agent runtime prepare --persistent-root {unlinked}",
|
|
793
|
+
data={"saved_state_folder": str(unlinked)},
|
|
794
|
+
)
|
|
795
|
+
raise typer.Exit(code=1)
|
|
771
796
|
emit_error(
|
|
772
797
|
"INIT",
|
|
773
798
|
code="no_session",
|
|
@@ -805,6 +830,17 @@ def status() -> None:
|
|
|
805
830
|
"Still waiting for your approval in Link. The booking finishes as soon as "
|
|
806
831
|
"it goes through."
|
|
807
832
|
)
|
|
833
|
+
elif session.flow_phase == "BUYER_RESOLVED" and not session.booking.id:
|
|
834
|
+
# Saved while waiting for the user's yes. A new conversation cannot see the recap the
|
|
835
|
+
# old one showed, so status hands it back instead of a `flow book` that would be refused.
|
|
836
|
+
awaiting = "booking_confirmation"
|
|
837
|
+
next_cmd = "booking-agent flow buyer resolve"
|
|
838
|
+
prompt = (
|
|
839
|
+
"A booking is waiting for the user's yes and this conversation has not shown them "
|
|
840
|
+
"the summary. Do not start over, search again, or list times. Run next_command: "
|
|
841
|
+
"it shows the same summary again and tells you what to ask. The user's next reply "
|
|
842
|
+
"answers only that question."
|
|
843
|
+
)
|
|
808
844
|
elif session.flow_phase == "PREREQ_OK" and session.discover_services:
|
|
809
845
|
# Results were already found (possibly in an earlier conversation): hand them back
|
|
810
846
|
# instead of making the agent search again and risk a reordered list.
|
|
@@ -3472,6 +3508,19 @@ def _require_calendar_offer(session: Session) -> None:
|
|
|
3472
3508
|
raise typer.Exit(code=1)
|
|
3473
3509
|
|
|
3474
3510
|
|
|
3511
|
+
def _after_calendar(session: Session, prompt: str) -> tuple:
|
|
3512
|
+
"""Prompt and next command after a calendar step: a late reconnect resumes the booking."""
|
|
3513
|
+
from ..calendar_preference import CALENDAR_RECONNECTED_LATE_PHASES
|
|
3514
|
+
|
|
3515
|
+
if session.flow_phase in CALENDAR_RECONNECTED_LATE_PHASES:
|
|
3516
|
+
return (
|
|
3517
|
+
f"{prompt} The time is already chosen: do not list times again. Continue the "
|
|
3518
|
+
"booking without asking the user again.",
|
|
3519
|
+
"booking-agent flow buyer resolve",
|
|
3520
|
+
)
|
|
3521
|
+
return f"{prompt} Ready to query availability.", "booking-agent flow availability"
|
|
3522
|
+
|
|
3523
|
+
|
|
3475
3524
|
def _emit_calendar_unavailable(session: Session, status: dict, mode: str, *, data: Optional[dict]) -> None:
|
|
3476
3525
|
session.calendar_technical_fallback = True
|
|
3477
3526
|
skip_calendar_gate_session_only(session, unasked=True)
|
|
@@ -3481,17 +3530,12 @@ def _emit_calendar_unavailable(session: Session, status: dict, mode: str, *, dat
|
|
|
3481
3530
|
out["calendar_mode"] = CALENDAR_MODE_NONE
|
|
3482
3531
|
out["calendar_checking_unavailable"] = True
|
|
3483
3532
|
out["stale_calendar_mode"] = mode
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
"available on this device, so calendar checking is unavailable for this "
|
|
3489
|
-
"booking. Tell the user that in one plain sentence, then continue. Ready to "
|
|
3490
|
-
"query availability."
|
|
3491
|
-
),
|
|
3492
|
-
next_command="booking-agent flow availability",
|
|
3493
|
-
data=out,
|
|
3533
|
+
prompt, next_command = _after_calendar(session, (
|
|
3534
|
+
"The saved Google Calendar access is no longer valid and reconnecting is not "
|
|
3535
|
+
"available on this device, so calendar checking is unavailable for this "
|
|
3536
|
+
"booking. Tell the user that in one plain sentence, then continue."
|
|
3494
3537
|
))
|
|
3538
|
+
emit(_agent_out(session, prompt=prompt, next_command=next_command, data=out))
|
|
3495
3539
|
|
|
3496
3540
|
|
|
3497
3541
|
def _emit_calendar_reconnect_if_stale(
|
|
@@ -3706,13 +3750,14 @@ def calendar_skip(
|
|
|
3706
3750
|
_stop_pending_sign_in()
|
|
3707
3751
|
if session_only:
|
|
3708
3752
|
skip_calendar_gate_session_only(session, unasked=no_route)
|
|
3753
|
+
prompt, next_command = _after_calendar(session, (
|
|
3754
|
+
"Continuing this booking without calendar checking; the saved calendar "
|
|
3755
|
+
"preference is unchanged."
|
|
3756
|
+
))
|
|
3709
3757
|
emit(_agent_out(
|
|
3710
3758
|
session,
|
|
3711
|
-
prompt=
|
|
3712
|
-
|
|
3713
|
-
"preference is unchanged. Ready to query availability."
|
|
3714
|
-
),
|
|
3715
|
-
next_command="booking-agent flow availability",
|
|
3759
|
+
prompt=prompt,
|
|
3760
|
+
next_command=next_command,
|
|
3716
3761
|
data={"calendar_state": "skipped", "calendar_mode": CALENDAR_MODE_NONE},
|
|
3717
3762
|
))
|
|
3718
3763
|
return
|
|
@@ -3721,11 +3766,8 @@ def calendar_skip(
|
|
|
3721
3766
|
state="skipped",
|
|
3722
3767
|
mode=CALENDAR_MODE_NONE,
|
|
3723
3768
|
)
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
prompt="Calendar gate satisfied (user opted out). Ready to query availability.",
|
|
3727
|
-
next_command="booking-agent flow availability",
|
|
3728
|
-
))
|
|
3769
|
+
prompt, next_command = _after_calendar(session, "Calendar gate satisfied (user opted out).")
|
|
3770
|
+
emit(_agent_out(session, prompt=prompt, next_command=next_command))
|
|
3729
3771
|
|
|
3730
3772
|
|
|
3731
3773
|
@calendar_app.command("connect")
|
|
@@ -3799,20 +3841,9 @@ def _emit_calendar_connected(session: Session, *, mode: str, data: dict) -> None
|
|
|
3799
3841
|
if mode == CALENDAR_MODE_EVENTS
|
|
3800
3842
|
else "Calendar connected for conflict checking (free/busy only)."
|
|
3801
3843
|
)
|
|
3802
|
-
|
|
3803
|
-
# Reconnected late (after the slot was chosen): pick the booking up where it stopped.
|
|
3804
|
-
emit(_agent_out(
|
|
3805
|
-
session,
|
|
3806
|
-
prompt=f"{prompt} Continue the booking without asking the user again.",
|
|
3807
|
-
next_command="booking-agent flow buyer resolve",
|
|
3808
|
-
data={**data, "mode": mode},
|
|
3809
|
-
))
|
|
3810
|
-
return
|
|
3844
|
+
prompt, next_command = _after_calendar(session, prompt)
|
|
3811
3845
|
emit(_agent_out(
|
|
3812
|
-
session,
|
|
3813
|
-
prompt=f"{prompt} Ready to query availability.",
|
|
3814
|
-
next_command="booking-agent flow availability",
|
|
3815
|
-
data={**data, "mode": mode},
|
|
3846
|
+
session, prompt=prompt, next_command=next_command, data={**data, "mode": mode},
|
|
3816
3847
|
))
|
|
3817
3848
|
|
|
3818
3849
|
|
|
@@ -5202,12 +5233,15 @@ def buyer_resolve(
|
|
|
5202
5233
|
"""Run `link-cli user-info retrieve` and store the Buyer in session."""
|
|
5203
5234
|
session = must_load()
|
|
5204
5235
|
try:
|
|
5205
|
-
|
|
5236
|
+
# BUYER_RESOLVED is allowed again: a new conversation (after a restart) or a corrected
|
|
5237
|
+
# note must be able to show the same recap, since no booking exists yet in that phase.
|
|
5238
|
+
require_phase(session, "SLOT_SELECTED", "BUYER_RESOLVED", code="no_slot_selected",
|
|
5206
5239
|
next_command="booking-agent flow slots select --slot-id <id> --user-confirmed")
|
|
5207
5240
|
_resync_link_auth_gate(session)
|
|
5208
5241
|
require_link_auth_gate(session)
|
|
5209
5242
|
except GateError as e:
|
|
5210
5243
|
raise _handle_gate(session, e)
|
|
5244
|
+
recap_shown_again = session.flow_phase == "BUYER_RESOLVED"
|
|
5211
5245
|
|
|
5212
5246
|
from ..link_cli import (
|
|
5213
5247
|
LinkCliUserInfoError,
|
|
@@ -5252,7 +5286,7 @@ def buyer_resolve(
|
|
|
5252
5286
|
session.booking_notes = " ".join(notes.split())[:BOOKING_NOTES_MAX_CHARS] or None
|
|
5253
5287
|
_ensure_buyer_timezone(session, link_cli_bin=link_cli_bin)
|
|
5254
5288
|
dual = _selected_slot_zones_differ(session)
|
|
5255
|
-
notice_needed = dual and not session.timezone_difference_shown
|
|
5289
|
+
notice_needed = dual and (recap_shown_again or not session.timezone_difference_shown)
|
|
5256
5290
|
if notice_needed:
|
|
5257
5291
|
session.timezone_difference_shown = True
|
|
5258
5292
|
transition(session, to="BUYER_RESOLVED")
|
{booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/src/booking_agent_cli/runtime_prepare.py
RENAMED
|
@@ -17,7 +17,7 @@ import sys
|
|
|
17
17
|
import tempfile
|
|
18
18
|
import time
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Dict, List, Optional, Tuple
|
|
20
|
+
from typing import Callable, Dict, List, Optional, Tuple
|
|
21
21
|
|
|
22
22
|
from booking_agent import settings
|
|
23
23
|
from booking_agent.secure_file import ensure_private_dir, write_private_text
|
|
@@ -353,6 +353,11 @@ def diagnose() -> Dict[str, object]:
|
|
|
353
353
|
findings.append({"kind": kind, "path": str(target / sub), "severity": "problem"})
|
|
354
354
|
else:
|
|
355
355
|
findings.append({"kind": "not_persistent", "path": str(state), "severity": "info"})
|
|
356
|
+
unlinked = find_unlinked_state()
|
|
357
|
+
if unlinked is not None:
|
|
358
|
+
problems = True
|
|
359
|
+
root_hint = str(unlinked)
|
|
360
|
+
findings.append({"kind": "saved_state_not_linked", "path": root_hint, "severity": "problem"})
|
|
356
361
|
if state_info["linked_to"] and link_info["linked_to"]:
|
|
357
362
|
if Path(str(state_info["linked_to"])).parent != Path(str(link_info["linked_to"])).parent:
|
|
358
363
|
problems = True
|
|
@@ -457,6 +462,84 @@ def persistence_resolution() -> Optional[str]:
|
|
|
457
462
|
return None
|
|
458
463
|
|
|
459
464
|
|
|
465
|
+
_TERMINAL_PHASES = frozenset({"CONFIRMED", "FAILED"})
|
|
466
|
+
_SCAN_DEPTH_NAMES = 2
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def _safe(check: Callable[[], bool]) -> bool:
|
|
470
|
+
"""A filesystem test that treats an unreadable path as absent (``Path.is_file`` raises on EACCES)."""
|
|
471
|
+
try:
|
|
472
|
+
return bool(check())
|
|
473
|
+
except OSError:
|
|
474
|
+
return False
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _holds_unfinished_booking(session_file: Path) -> bool:
|
|
478
|
+
try:
|
|
479
|
+
raw = json.loads(session_file.read_text())
|
|
480
|
+
except (OSError, ValueError):
|
|
481
|
+
return False
|
|
482
|
+
sessions = raw.get("sessions") if isinstance(raw, dict) else None
|
|
483
|
+
return any(
|
|
484
|
+
isinstance(s, dict) and s.get("flow_phase") not in _TERMINAL_PHASES
|
|
485
|
+
for s in sessions or []
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def find_unlinked_state() -> Optional[Path]:
|
|
490
|
+
"""A persistent root that holds an unfinished booking this runtime has not linked.
|
|
491
|
+
|
|
492
|
+
A restart can wipe the runtime's own folders (and the symlinks into the persistent one),
|
|
493
|
+
leaving the saved booking and Link sign-in intact but invisible: the CLI then sees "no
|
|
494
|
+
session" and an agent starts over. Looks beside the working folder, its parents, and the
|
|
495
|
+
home folder, at most two levels down, for a folder this CLI prepared earlier (it holds
|
|
496
|
+
``booking-agent/.usp-session.json`` and a boot marker or the sign-in folder).
|
|
497
|
+
"""
|
|
498
|
+
home = settings.booking_agent_home()
|
|
499
|
+
if _safe(home.is_symlink) or _safe((home / _SESSION_FILE).is_file):
|
|
500
|
+
return None
|
|
501
|
+
try:
|
|
502
|
+
bases = [Path.cwd(), *Path.cwd().parents, Path.home()]
|
|
503
|
+
except (OSError, RuntimeError):
|
|
504
|
+
return None
|
|
505
|
+
seen = set()
|
|
506
|
+
for base in bases:
|
|
507
|
+
if base in seen:
|
|
508
|
+
continue
|
|
509
|
+
seen.add(base)
|
|
510
|
+
level = [base]
|
|
511
|
+
for _ in range(_SCAN_DEPTH_NAMES + 1):
|
|
512
|
+
nxt = []
|
|
513
|
+
for folder in level:
|
|
514
|
+
state = folder / STATE_SUBDIR
|
|
515
|
+
session_file = state / _SESSION_FILE
|
|
516
|
+
if (
|
|
517
|
+
_safe(session_file.is_file)
|
|
518
|
+
and (_safe((folder / _BOOT_FILE).is_file) or _safe((folder / LINK_SUBDIR).is_dir))
|
|
519
|
+
and _holds_unfinished_booking(session_file)
|
|
520
|
+
):
|
|
521
|
+
return folder
|
|
522
|
+
try:
|
|
523
|
+
nxt.extend(
|
|
524
|
+
child for child in sorted(folder.iterdir())
|
|
525
|
+
if _safe(child.is_dir) and not _safe(child.is_symlink)
|
|
526
|
+
and child.name not in ("node_modules", ".git", ".venv", "proc", "sys")
|
|
527
|
+
)
|
|
528
|
+
except OSError:
|
|
529
|
+
continue
|
|
530
|
+
level = nxt if len(nxt) <= 200 else []
|
|
531
|
+
return None
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def saved_state_not_linked_message(root: Path) -> str:
|
|
535
|
+
return (
|
|
536
|
+
f"A saved booking and Link sign-in exist in {root}, but this runtime is not linked to "
|
|
537
|
+
"them (a restart cleared the links). Do not start over and do not use "
|
|
538
|
+
"--no-persistent-root: run next_command once, then continue with `booking-agent flow "
|
|
539
|
+
"status`."
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
|
|
460
543
|
def acknowledge_no_persistence() -> None:
|
|
461
544
|
home = ensure_private_dir(settings.booking_agent_home())
|
|
462
545
|
write_private_text(home / _ACK_FILE, time.strftime("%Y-%m-%d\n", time.gmtime()))
|
{booking_agent_cli-0.10.0 → booking_agent_cli-0.10.2}/tests/integration/test_cli_enforced_gates.py
RENAMED
|
@@ -658,3 +658,53 @@ def test_compliance_flags_a_booking_that_skipped_a_confirmation(change, failure)
|
|
|
658
658
|
failures = _skill_compliance_failures(session)
|
|
659
659
|
|
|
660
660
|
assert any(f.startswith(failure) for f in failures), failures
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
# ---------------------------------------------------------------------------
|
|
664
|
+
# A new conversation resumes a booking that is waiting for the user's yes
|
|
665
|
+
# ---------------------------------------------------------------------------
|
|
666
|
+
|
|
667
|
+
def test_status_hands_back_the_summary_step_for_a_booking_waiting_for_the_yes(
|
|
668
|
+
runner, isolated_session, monkeypatch,
|
|
669
|
+
):
|
|
670
|
+
_direct_booking_to_recap(runner, monkeypatch)
|
|
671
|
+
|
|
672
|
+
body = _stdout_json(_invoke(runner, "flow", "status"))
|
|
673
|
+
|
|
674
|
+
assert body["phase"] == "BUYER_RESOLVED"
|
|
675
|
+
assert body["awaiting_user"] == "booking_confirmation"
|
|
676
|
+
assert body["next_command"] == "booking-agent flow buyer resolve"
|
|
677
|
+
assert "start over" in body["prompt"]
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
def test_the_summary_can_be_shown_again_after_a_restart_and_then_confirmed(
|
|
681
|
+
runner, isolated_session, monkeypatch,
|
|
682
|
+
):
|
|
683
|
+
first = _direct_booking_to_recap(runner, monkeypatch)
|
|
684
|
+
|
|
685
|
+
again = _invoke(runner, "flow", "buyer", "resolve")
|
|
686
|
+
|
|
687
|
+
assert again.exit_code == 0, again.stdout + again.stderr
|
|
688
|
+
body = _stdout_json(again)
|
|
689
|
+
assert body["awaiting_user"] == "booking_confirmation"
|
|
690
|
+
assert body["data"]["booking_recap_lines"] == first["data"]["booking_recap_lines"]
|
|
691
|
+
assert load_session().recap_confirmed_digest is None
|
|
692
|
+
r = _invoke(runner, *_BOOK_CONFIRMED)
|
|
693
|
+
assert r.exit_code == 0, r.stdout + r.stderr
|
|
694
|
+
assert load_session().booking.id
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def test_the_next_command_after_a_changed_summary_actually_runs(
|
|
698
|
+
runner, isolated_session, monkeypatch,
|
|
699
|
+
):
|
|
700
|
+
_direct_booking_to_recap(runner, monkeypatch)
|
|
701
|
+
session = load_session()
|
|
702
|
+
session.recap_digest = "0000000000000000"
|
|
703
|
+
save_session(session)
|
|
704
|
+
refused = _stdout_json(_invoke(runner, *_BOOK_CONFIRMED))
|
|
705
|
+
assert refused["next_command"].startswith("booking-agent flow buyer resolve")
|
|
706
|
+
|
|
707
|
+
r = _invoke(runner, "flow", "buyer", "resolve")
|
|
708
|
+
|
|
709
|
+
assert r.exit_code == 0, r.stdout + r.stderr
|
|
710
|
+
assert _stdout_json(r)["awaiting_user"] == "booking_confirmation"
|