booking-agent-cli 0.10.1__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.1 → booking_agent_cli-0.10.2}/PKG-INFO +2 -2
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/README.md +1 -1
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/pyproject.toml +1 -1
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/__init__.py +1 -1
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/__init__.py +1 -1
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/bundled_skill.md +20 -22
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/calendar_preference.py +16 -4
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/flow.py +53 -33
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/runtime_prepare.py +84 -1
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_resume_after_restart.py +158 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/.gitignore +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/authorization.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/backend/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/backend/live.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/filter.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/google.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/oauth_listener.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/scope.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/circuit_breaker.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/config.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/defaults.env +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/discovery.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/faults.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/headless.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/http_deadlines.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/http_retry.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/http_trace.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/merchant_pacing.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/models/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/models/platform.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/models/wire.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/namespace.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/observability.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/platform_profile.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/pop.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/profile_cache.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/registry_url.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/rest_client.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/secure_file.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/settings.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/signals.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/step_timing.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_capabilities.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_checkout.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_negotiation.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/ucp_profile.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/usp_methods.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/activity_log.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/booking_poll.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/booking_summary.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/calendar_connection.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/calendar_event.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/catalog.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/checkout_prereq.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/config.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/lowlevel.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/runtime.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/commands/update.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/config_loader.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/engine.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/fault_report_schema.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/fault_reports.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/flow_timings.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/gates.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/geo_eligibility.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/geocode.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/identity.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/install_integrity.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/link_cli.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/main.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/output.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/payment_helpers.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/payment_token.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/provenance.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/reschedule.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/runtime.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/search_area.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/session.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/skill_bundle.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/skill_integrity.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/support_bundle_schema.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/support_report.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/timezone_context.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent_cli/updates.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/.env.tests +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/conftest.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/test_cancel_reschedule_e2e.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/test_latency_soak.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/test_staging_registry_integration.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/test_timezone_booking_e2e.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/test_ucp_native_e2e.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/test_ucp_skill_flow_e2e.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/e2e/ucp_native.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_activity_log.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_at_rest_hygiene.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_booking_rejection.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_bulk_cancel.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_connection.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_connector.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_flow.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_calendar_preference.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_checkout_signals.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_circuit_breaker.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_cli_enforced_gates.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_config.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_correlation_flow.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_declined_spt.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_discover_registry_rejection.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_flow_evidence.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_flow_gates.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_flow_reschedule.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_flow_step_timings.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_google_calendar_oauth.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_home_visit_address.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_http_trace.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_identity_logout.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_install_integrity.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_lazy_oauth.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_link_auth.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_link_cli_spt.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_namespace.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_oauth_listener.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_oauth_metadata.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_platform_key_pop.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_platform_profile.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_pop_keystore.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_profile_cache.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_regression_index.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_release_hold_uses_delete.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_request_retry_semantics.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_runtime_prepare.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_search_area.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_session_compat.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_session_retention.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_skill_integrity.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_skill_portability.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_support_report.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_timezone_context.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_cancel_result.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_checkout_pop.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_checkout_proof_wireup.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_checkout_validate.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_ucp_profile.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_update_command.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/integration/test_wheel_contents.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/SOURCE +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/acp_booking_extension.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/availability.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/booking.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/calendar_freebusy.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/catalog.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/paid_bookings.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/profile.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/registry.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/rest_common.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/usp.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/waitlist.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/spec_schemas/webhook_event.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/fixtures/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/fixtures/profiles.py +0 -0
- {booking_agent_cli-0.10.1 → 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.1 → booking_agent_cli-0.10.2}/tests/support/fixtures/services.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/backend.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/link_cli.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/mock_calendar.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/pop_business.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/pop_transport.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/ucp_checkout_store.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/mock/ucp_server.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/offline_stack/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/support/offline_stack/stack.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/__init__.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_authorization_policy.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_booking_poll_credentials.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_booking_request_error.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_calendar_filter.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_catalog.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_check_version.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_conditional_writes.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_delivery_address.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_discovery.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_fault_reports.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_geo_eligibility.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_geocode.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_headless.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_http_deadlines.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_http_retry.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_live_search_registry.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_merchant_pacing.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_mock_support.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_money_path_events.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_nonce_from_pop_error_body.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_observability.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_outbound_member_names.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_package_layering.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_pay_at_service.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_provenance.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_registry_search_filters.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_registry_search_pricing.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_registry_search_responses.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_registry_url.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_release_notes.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_rest_client.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_scoped_method_authorization.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_session_credentials.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_spec_conformance.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_step_timing.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_capabilities.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_complete_outcome.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_create_price_guard.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_ucp_negotiation.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_updates.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/unit/test_wire_model_conformance.py +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/001-jcs-nested-buyer.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/002-jcs-meta-only.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/003-jcs-unicode-empty-meta.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/004-proof-issuance-no-ath.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/005-proof-presentation-ath-nonce.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/006-cross-key-replay.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/007-downgrade-cnf-without-proof.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/008-replayed-jti.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/009-aud-mismatch.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/010-alg-none.json +0 -0
- {booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/tests/vectors/pop/README.md +0 -0
- {booking_agent_cli-0.10.1 → 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.1 → 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.1 → 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.1 → 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",
|
|
@@ -3483,6 +3508,19 @@ def _require_calendar_offer(session: Session) -> None:
|
|
|
3483
3508
|
raise typer.Exit(code=1)
|
|
3484
3509
|
|
|
3485
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
|
+
|
|
3486
3524
|
def _emit_calendar_unavailable(session: Session, status: dict, mode: str, *, data: Optional[dict]) -> None:
|
|
3487
3525
|
session.calendar_technical_fallback = True
|
|
3488
3526
|
skip_calendar_gate_session_only(session, unasked=True)
|
|
@@ -3492,17 +3530,12 @@ def _emit_calendar_unavailable(session: Session, status: dict, mode: str, *, dat
|
|
|
3492
3530
|
out["calendar_mode"] = CALENDAR_MODE_NONE
|
|
3493
3531
|
out["calendar_checking_unavailable"] = True
|
|
3494
3532
|
out["stale_calendar_mode"] = mode
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
"available on this device, so calendar checking is unavailable for this "
|
|
3500
|
-
"booking. Tell the user that in one plain sentence, then continue. Ready to "
|
|
3501
|
-
"query availability."
|
|
3502
|
-
),
|
|
3503
|
-
next_command="booking-agent flow availability",
|
|
3504
|
-
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."
|
|
3505
3537
|
))
|
|
3538
|
+
emit(_agent_out(session, prompt=prompt, next_command=next_command, data=out))
|
|
3506
3539
|
|
|
3507
3540
|
|
|
3508
3541
|
def _emit_calendar_reconnect_if_stale(
|
|
@@ -3717,13 +3750,14 @@ def calendar_skip(
|
|
|
3717
3750
|
_stop_pending_sign_in()
|
|
3718
3751
|
if session_only:
|
|
3719
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
|
+
))
|
|
3720
3757
|
emit(_agent_out(
|
|
3721
3758
|
session,
|
|
3722
|
-
prompt=
|
|
3723
|
-
|
|
3724
|
-
"preference is unchanged. Ready to query availability."
|
|
3725
|
-
),
|
|
3726
|
-
next_command="booking-agent flow availability",
|
|
3759
|
+
prompt=prompt,
|
|
3760
|
+
next_command=next_command,
|
|
3727
3761
|
data={"calendar_state": "skipped", "calendar_mode": CALENDAR_MODE_NONE},
|
|
3728
3762
|
))
|
|
3729
3763
|
return
|
|
@@ -3732,11 +3766,8 @@ def calendar_skip(
|
|
|
3732
3766
|
state="skipped",
|
|
3733
3767
|
mode=CALENDAR_MODE_NONE,
|
|
3734
3768
|
)
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
prompt="Calendar gate satisfied (user opted out). Ready to query availability.",
|
|
3738
|
-
next_command="booking-agent flow availability",
|
|
3739
|
-
))
|
|
3769
|
+
prompt, next_command = _after_calendar(session, "Calendar gate satisfied (user opted out).")
|
|
3770
|
+
emit(_agent_out(session, prompt=prompt, next_command=next_command))
|
|
3740
3771
|
|
|
3741
3772
|
|
|
3742
3773
|
@calendar_app.command("connect")
|
|
@@ -3810,20 +3841,9 @@ def _emit_calendar_connected(session: Session, *, mode: str, data: dict) -> None
|
|
|
3810
3841
|
if mode == CALENDAR_MODE_EVENTS
|
|
3811
3842
|
else "Calendar connected for conflict checking (free/busy only)."
|
|
3812
3843
|
)
|
|
3813
|
-
|
|
3814
|
-
# Reconnected late (after the slot was chosen): pick the booking up where it stopped.
|
|
3815
|
-
emit(_agent_out(
|
|
3816
|
-
session,
|
|
3817
|
-
prompt=f"{prompt} Continue the booking without asking the user again.",
|
|
3818
|
-
next_command="booking-agent flow buyer resolve",
|
|
3819
|
-
data={**data, "mode": mode},
|
|
3820
|
-
))
|
|
3821
|
-
return
|
|
3844
|
+
prompt, next_command = _after_calendar(session, prompt)
|
|
3822
3845
|
emit(_agent_out(
|
|
3823
|
-
session,
|
|
3824
|
-
prompt=f"{prompt} Ready to query availability.",
|
|
3825
|
-
next_command="booking-agent flow availability",
|
|
3826
|
-
data={**data, "mode": mode},
|
|
3846
|
+
session, prompt=prompt, next_command=next_command, data={**data, "mode": mode},
|
|
3827
3847
|
))
|
|
3828
3848
|
|
|
3829
3849
|
|
{booking_agent_cli-0.10.1 → 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.1 → booking_agent_cli-0.10.2}/tests/integration/test_resume_after_restart.py
RENAMED
|
@@ -238,3 +238,161 @@ def test_doctor_says_why_a_restart_is_not_shown(isolated_session, monkeypatch, t
|
|
|
238
238
|
persistence = rp.diagnose()["persistence"]
|
|
239
239
|
assert persistence["survived_restart"] is True
|
|
240
240
|
assert persistence["survived_restart_basis"] == "second_runtime_read_the_saved_booking"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# ---------------------------------------------------------------------------
|
|
244
|
+
# A restart cleared the links to the saved state: the booking is still there
|
|
245
|
+
# ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
def _restart_that_clears_the_links(tmp_path, monkeypatch, *, marker=True):
|
|
248
|
+
"""Move the saved booking into a persistent root, then start a runtime with empty folders.
|
|
249
|
+
|
|
250
|
+
Hosted replay of 0.10.0 on Base44 Superagent: the sandbox was wiped except the saved
|
|
251
|
+
folder, the new runtime saw "no session", and the assistant started over.
|
|
252
|
+
"""
|
|
253
|
+
import shutil
|
|
254
|
+
|
|
255
|
+
root = tmp_path / "app" / ".agents" / "state"
|
|
256
|
+
(root / "booking-agent").mkdir(parents=True)
|
|
257
|
+
shutil.move(str(tmp_path / ".usp-session.json"), str(root / "booking-agent" / ".usp-session.json"))
|
|
258
|
+
if marker:
|
|
259
|
+
(root / ".booking-agent-boot").write_text("{}")
|
|
260
|
+
conversation = tmp_path / "app" / "conversations" / "c1"
|
|
261
|
+
conversation.mkdir(parents=True)
|
|
262
|
+
runtime_home = tmp_path / "rt" / ".booking-agent"
|
|
263
|
+
monkeypatch.setenv("BOOKING_AGENT_SESSION_DIR", str(runtime_home))
|
|
264
|
+
monkeypatch.setenv("HOME", str(tmp_path / "rt"))
|
|
265
|
+
monkeypatch.setattr(
|
|
266
|
+
"booking_agent_cli.runtime_prepare.link_cli_config_dir", lambda: tmp_path / "rt" / "link",
|
|
267
|
+
)
|
|
268
|
+
monkeypatch.chdir(conversation)
|
|
269
|
+
return root
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_status_points_at_saved_state_this_runtime_is_not_linked_to(
|
|
273
|
+
runner, isolated_session, monkeypatch, tmp_path,
|
|
274
|
+
):
|
|
275
|
+
_at_summary(runner, monkeypatch)
|
|
276
|
+
root = _restart_that_clears_the_links(tmp_path, monkeypatch)
|
|
277
|
+
|
|
278
|
+
out = _stdout_json(_invoke(runner, "flow", "status"))
|
|
279
|
+
|
|
280
|
+
assert out["data"]["error"]["code"] == "saved_state_not_linked"
|
|
281
|
+
assert out["next_command"] == f"booking-agent runtime prepare --persistent-root {root}"
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def test_init_will_not_start_over_beside_saved_state(
|
|
285
|
+
runner, isolated_session, monkeypatch, tmp_path,
|
|
286
|
+
):
|
|
287
|
+
_at_summary(runner, monkeypatch)
|
|
288
|
+
root = _restart_that_clears_the_links(tmp_path, monkeypatch)
|
|
289
|
+
monkeypatch.delenv("BOOKING_AGENT_PERSISTENCE") # the test default declares "none"
|
|
290
|
+
|
|
291
|
+
for flag in ("--no-persistent-root", None):
|
|
292
|
+
args = ["flow", "init", "--user-request", "AC repair", "--skill-version", "0.0.0"]
|
|
293
|
+
if flag:
|
|
294
|
+
args.append(flag)
|
|
295
|
+
r = _invoke(runner, *args)
|
|
296
|
+
assert r.exit_code == 1, r.stdout
|
|
297
|
+
out = _stdout_json(r)
|
|
298
|
+
assert out["data"]["error"]["code"] == "saved_state_not_linked"
|
|
299
|
+
assert out["next_command"].endswith(str(root))
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def test_linking_the_folder_brings_the_waiting_booking_back(
|
|
303
|
+
runner, isolated_session, monkeypatch, tmp_path,
|
|
304
|
+
):
|
|
305
|
+
_at_summary(runner, monkeypatch)
|
|
306
|
+
root = _restart_that_clears_the_links(tmp_path, monkeypatch)
|
|
307
|
+
from booking_agent_cli.main import app
|
|
308
|
+
|
|
309
|
+
r = runner.invoke(app, ["runtime", "prepare", "--persistent-root", str(root)])
|
|
310
|
+
assert r.exit_code == 0, r.stdout
|
|
311
|
+
|
|
312
|
+
out = _stdout_json(_invoke(runner, "flow", "status"))
|
|
313
|
+
|
|
314
|
+
assert out["phase"] == "BUYER_RESOLVED"
|
|
315
|
+
assert out["awaiting_user"] == "booking_confirmation"
|
|
316
|
+
assert out["next_command"] == "booking-agent flow buyer resolve"
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_doctor_reports_saved_state_that_is_not_linked(
|
|
320
|
+
runner, isolated_session, monkeypatch, tmp_path,
|
|
321
|
+
):
|
|
322
|
+
_at_summary(runner, monkeypatch)
|
|
323
|
+
root = _restart_that_clears_the_links(tmp_path, monkeypatch)
|
|
324
|
+
from booking_agent_cli.main import app
|
|
325
|
+
|
|
326
|
+
r = runner.invoke(app, ["runtime", "doctor"])
|
|
327
|
+
out = _stdout_json(r)
|
|
328
|
+
|
|
329
|
+
kinds = {f["kind"]: f for f in out["data"]["findings"]}
|
|
330
|
+
assert kinds["saved_state_not_linked"]["path"] == str(root)
|
|
331
|
+
assert out["data"]["healthy"] is False
|
|
332
|
+
assert out["next_command"] == f"booking-agent runtime prepare --persistent-root {root}"
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_a_folder_this_cli_never_prepared_is_not_mistaken_for_saved_state(
|
|
336
|
+
runner, isolated_session, monkeypatch, tmp_path,
|
|
337
|
+
):
|
|
338
|
+
_at_summary(runner, monkeypatch)
|
|
339
|
+
_restart_that_clears_the_links(tmp_path, monkeypatch, marker=False)
|
|
340
|
+
|
|
341
|
+
out = _stdout_json(_invoke(runner, "flow", "status"))
|
|
342
|
+
|
|
343
|
+
assert out["data"]["error"]["code"] == "no_session"
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# ---------------------------------------------------------------------------
|
|
347
|
+
# Reconnecting the calendar after a restart must not drop the chosen time
|
|
348
|
+
# ---------------------------------------------------------------------------
|
|
349
|
+
|
|
350
|
+
def test_a_calendar_step_after_the_summary_keeps_the_chosen_time(
|
|
351
|
+
runner, isolated_session, monkeypatch,
|
|
352
|
+
):
|
|
353
|
+
"""Hosted replay of 0.10.1: `calendar connect` at the summary sent the booking back to
|
|
354
|
+
CALENDAR_GATE, so the slot and summary were lost and times were listed again."""
|
|
355
|
+
slot_id = _at_summary(runner, monkeypatch)
|
|
356
|
+
|
|
357
|
+
r = _invoke(runner, "flow", "calendar", "skip", "--session-only")
|
|
358
|
+
|
|
359
|
+
assert r.exit_code == 0, r.stdout + r.stderr
|
|
360
|
+
out = _stdout_json(r)
|
|
361
|
+
assert out["phase"] == "BUYER_RESOLVED"
|
|
362
|
+
assert out["next_command"] == "booking-agent flow buyer resolve"
|
|
363
|
+
assert "do not list times again" in out["prompt"]
|
|
364
|
+
from booking_agent_cli.session import load_session
|
|
365
|
+
|
|
366
|
+
assert load_session().slot_id == slot_id
|
|
367
|
+
again = _invoke(runner, "flow", "buyer", "resolve")
|
|
368
|
+
assert again.exit_code == 0, again.stdout + again.stderr
|
|
369
|
+
assert _stdout_json(again)["awaiting_user"] == "booking_confirmation"
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def test_the_calendar_gate_still_leads_to_availability_early_in_the_booking(
|
|
373
|
+
runner, isolated_session,
|
|
374
|
+
):
|
|
375
|
+
_init(runner)
|
|
376
|
+
_complete_checkout_mode_setup(runner)
|
|
377
|
+
|
|
378
|
+
out = _stdout_json(_skip_calendar(runner, "--session-only"))
|
|
379
|
+
|
|
380
|
+
assert out["phase"] == "CALENDAR_GATE"
|
|
381
|
+
assert out["next_command"] == "booking-agent flow availability"
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def test_looking_for_saved_state_survives_a_folder_it_cannot_read(
|
|
385
|
+
runner, isolated_session, monkeypatch, tmp_path,
|
|
386
|
+
):
|
|
387
|
+
"""CI runners have folders the user cannot read; `Path.is_file` raises on those."""
|
|
388
|
+
from pathlib import Path
|
|
389
|
+
|
|
390
|
+
import booking_agent_cli.runtime_prepare as rp
|
|
391
|
+
|
|
392
|
+
def _denied(self, *args, **kwargs):
|
|
393
|
+
raise PermissionError(13, "Permission denied", str(self))
|
|
394
|
+
|
|
395
|
+
monkeypatch.setattr(Path, "is_file", _denied)
|
|
396
|
+
monkeypatch.setattr(Path, "is_dir", _denied)
|
|
397
|
+
|
|
398
|
+
assert rp.find_unlinked_state() is None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{booking_agent_cli-0.10.1 → booking_agent_cli-0.10.2}/src/booking_agent/calendar/oauth_listener.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|