fruxon 2.2.0__tar.gz → 2.2.1__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.
- {fruxon-2.2.0 → fruxon-2.2.1}/HISTORY.md +19 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/PKG-INFO +1 -1
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/_version.py +2 -2
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_memory.py +30 -7
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/examples.py +8 -2
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/fruxon.py +42 -10
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_client.py +38 -3
- {fruxon-2.2.0 → fruxon-2.2.1}/.gitignore +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/LICENSE +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/README.md +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/pyproject.toml +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/__init__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/__main__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/_ssl.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/checks/__init__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/checks/agent.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/checks/ledger.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/checks/references.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/__init__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/_crash.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/_schema.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/_shared.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/_stream.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_approvals.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_budget.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_channels.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_draft.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_evaluations.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_executions.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_improvements.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_lifecycle.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_reply_style.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_revisions.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_sandbox.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_sandbox_test.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_slots.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_system_blocks.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_tests.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_topics.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/agents_voice_calls.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/applications.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/applications_entry_points.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/assets.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/auth.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/capabilities.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/completion.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/config.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/consult_pins.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/conversation_imports.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/dash.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/describe.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/doctor.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/environments.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/escalations.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/guides.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/integrations.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/keys.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/knowledge_bases.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/knowledge_documents.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/knowledge_notices.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/knowledge_sources.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/llm_providers.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/messages.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/metrics.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/participants.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/pipelines.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/secrets.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/skills.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/storage.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/tools.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/triggers.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/triggers_budget.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/triggers_check.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/triggers_ledger.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/triggers_ledger_stage_tests.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/triggers_questions.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/triggers_shape.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/cli/workspaces.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/credentials.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/device_auth.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/doctor.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/exceptions.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/flow_validation.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/models.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/output.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/params.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/sandbox_scenario.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/skills/__init__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/skills/fruxon-agent-mode/SKILL.md +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/skills/fruxon-build-agent/SKILL.md +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/skills/fruxon-create-integration/SKILL.md +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/skills/fruxon-debug-trace/SKILL.md +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/skills/fruxon-meet/SKILL.md +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/skills/fruxon-use-integrations/SKILL.md +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/telemetry.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/__init__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/app.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/demo.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/links.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/__init__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/agent.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/base.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/home.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/ledger.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/loader.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/pickers.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/screens/trace.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/source.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/theme.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/views.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/tui/widgets.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/ui.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/update_check.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/src/fruxon/validation.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/.test_durations.json +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/__init__.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/conftest.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_actor.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_agent_lifecycle.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_agent_revisions_list.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_agent_slots.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_agents_check_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_agents_revisions_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_agents_sandbox.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_applications.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_approvals.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_assets.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_budgets.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_capabilities.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_channels.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_checks_agent.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_checks_ledger.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_cli_base_url_routing.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_cli_crash.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_cli_root_flags.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_connect_nudge.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_consult.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_conversation_imports.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_credentials.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_dash_app.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_dash_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_dash_client.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_dash_links.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_dash_source.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_describe.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_device_auth.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_docs_commands_exist.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_doctor.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_draft_bootstrap.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_draft_evaluate_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_draft_pull_shape.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_draft_run_replay.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_draft_validate_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_drafts.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_entry_points.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_environments.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_escalations.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_evaluations.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_execution_records.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_flow_validation.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_fruxon.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_guides.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_improvements.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_integration_configs_write_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_integration_triggers_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_integrations_authorize_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_integrations_connect_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_kb_trial_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_knowledge_bases.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_knowledge_documents.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_knowledge_notices.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_knowledge_sources.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_llm_provider_configs_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_llm_providers_connect_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_managed_by.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_memory.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_messages.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_metrics.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_online_llm_config_refs.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_online_prefetch_refs.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_online_secret_refs.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_output.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_params.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_participants.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_participants_write.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_pipelines.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_reply_style.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_sandbox_scenario.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_schema.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_schema_fetch.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_secrets.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_skills.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_ssl.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_storage.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_system_blocks.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_system_blocks_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_telemetry.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_test_chats.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_topics.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_trigger_budgets.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers_check_cli.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers_ledger.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers_ledger_batch_views.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers_ledger_stage_tests.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers_questions.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers_shape.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_triggers_write.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_tui_boundary.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_ui.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_update_check.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_validation.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_voice_calls.py +0 -0
- {fruxon-2.2.0 → fruxon-2.2.1}/tests/test_workspaces_cli.py +0 -0
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# History
|
|
2
2
|
|
|
3
|
+
## Unreleased — `chat_user` is deprecated: the server ignores it
|
|
4
|
+
|
|
5
|
+
- `execute`, `stream` and `stream_text` no longer send `chat_user` (as
|
|
6
|
+
`chatUser`). The execution endpoints have no field for it, so the server
|
|
7
|
+
dropped it unread: it did not identify the end user to the agent, gave no
|
|
8
|
+
per-user memory and filled no `{{sender.*}}` values. The argument is still
|
|
9
|
+
accepted, so existing calls keep working, but passing it emits a
|
|
10
|
+
`DeprecationWarning`. To give the agent details about the user, pass them in
|
|
11
|
+
`parameters`.
|
|
12
|
+
|
|
13
|
+
## Unreleased — `agents memory` names a person by participant id or address
|
|
14
|
+
|
|
15
|
+
- `fruxon agents memory forget-subject` and `list --subject` document the
|
|
16
|
+
subject the server takes: the person's participant id, or one of their
|
|
17
|
+
addresses as `provider:identifier` (e.g. `telegram:123456789`). The old
|
|
18
|
+
example, `user_42`, matched nothing. With fruxon-backend's fix either form
|
|
19
|
+
reaches every memory about the person — those filed under the participant
|
|
20
|
+
id since memory follows the person, and older ones filed under an address.
|
|
21
|
+
|
|
3
22
|
## Unreleased — whoami and the 403 hint read the role cap off the key
|
|
4
23
|
|
|
5
24
|
- On a server with fruxon-backend #1571, `fruxon whoami` and the 403
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fruxon
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: The Fruxon SDK is a lightweight Python client for integrating with the Fruxon platform.
|
|
5
5
|
Project-URL: bugs, https://github.com/fruxon-ai/fruxon-sdk/issues
|
|
6
6
|
Project-URL: changelog, https://github.com/fruxon-ai/fruxon-sdk/blob/main/HISTORY.md
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 2,
|
|
21
|
+
__version__ = version = '2.2.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -51,7 +51,13 @@ def memory_list(
|
|
|
51
51
|
agent: Annotated[str, typer.Argument(help="Agent whose memories to list.")],
|
|
52
52
|
subject: Annotated[
|
|
53
53
|
str | None,
|
|
54
|
-
typer.Option(
|
|
54
|
+
typer.Option(
|
|
55
|
+
"--subject",
|
|
56
|
+
help=(
|
|
57
|
+
"Only memories about this person: their participant id, or a channel address as "
|
|
58
|
+
"provider:identifier (e.g. telegram:123456789)."
|
|
59
|
+
),
|
|
60
|
+
),
|
|
55
61
|
] = None,
|
|
56
62
|
query: Annotated[str | None, typer.Option("--query", "-q", help="Full-text filter over title + content.")] = None,
|
|
57
63
|
scope: Annotated[
|
|
@@ -75,10 +81,13 @@ def memory_list(
|
|
|
75
81
|
"""List what an agent remembers — narrow with --subject / --query / --scope.
|
|
76
82
|
|
|
77
83
|
The filters run server-side, so "what does my agent remember about
|
|
78
|
-
|
|
84
|
+
the Telegram user 123456789" is `--subject telegram:123456789`, not a
|
|
85
|
+
client-side scan. A subject is the person's participant id or one of
|
|
86
|
+
their addresses as provider:identifier; either matches every memory
|
|
87
|
+
about them.
|
|
79
88
|
|
|
80
89
|
Examples:
|
|
81
|
-
fruxon agents memory list my-agent --subject
|
|
90
|
+
fruxon agents memory list my-agent --subject telegram:123456789
|
|
82
91
|
fruxon agents memory list my-agent --query "dark mode"
|
|
83
92
|
fruxon agents memory list my-agent --scope AGENT --output id
|
|
84
93
|
"""
|
|
@@ -228,7 +237,15 @@ def memory_get(
|
|
|
228
237
|
@memory_app.command("forget-subject")
|
|
229
238
|
def memory_forget_subject(
|
|
230
239
|
agent: Annotated[str, typer.Argument(help="Agent whose memories to prune.")],
|
|
231
|
-
subject: Annotated[
|
|
240
|
+
subject: Annotated[
|
|
241
|
+
str,
|
|
242
|
+
typer.Argument(
|
|
243
|
+
help=(
|
|
244
|
+
"The person to forget entirely: their participant id, or a channel address as "
|
|
245
|
+
"provider:identifier (e.g. telegram:123456789)."
|
|
246
|
+
)
|
|
247
|
+
),
|
|
248
|
+
],
|
|
232
249
|
yes: Annotated[
|
|
233
250
|
bool,
|
|
234
251
|
typer.Option("--yes", "-y", help="Skip the confirmation prompt. REQUIRED under agent mode (CLAUDECODE / CI)."),
|
|
@@ -242,11 +259,17 @@ def memory_forget_subject(
|
|
|
242
259
|
):
|
|
243
260
|
"""Forget every memory an agent holds about one subject (GDPR-style).
|
|
244
261
|
|
|
245
|
-
Irreversible — it deletes all of the
|
|
246
|
-
|
|
262
|
+
Irreversible — it deletes all of the person's memories, whether they
|
|
263
|
+
are filed under the participant id or under any of the person's
|
|
264
|
+
addresses, and records each deletion in the agent's deletion trail.
|
|
265
|
+
Confirms first, and under agent mode refuses without [bold]--yes[/bold].
|
|
266
|
+
|
|
267
|
+
Name the person by participant id (the `subject` column of
|
|
268
|
+
`agents memory subjects`) or by an address as provider:identifier.
|
|
247
269
|
|
|
248
270
|
Examples:
|
|
249
|
-
fruxon agents memory forget-subject my-agent
|
|
271
|
+
fruxon agents memory forget-subject my-agent telegram:123456789 --yes
|
|
272
|
+
fruxon agents memory forget-subject my-agent 0199f3c2-5b1e-7c4a-9d2e-3f6a8b1c0d4e --yes
|
|
250
273
|
"""
|
|
251
274
|
output = resolve_output_format(output, human_default="text", agent_default="json")
|
|
252
275
|
if output not in {"text", "json"}:
|
|
@@ -384,10 +384,16 @@ _EXAMPLES: dict[str, list[tuple[str, str]]] = {
|
|
|
384
384
|
),
|
|
385
385
|
],
|
|
386
386
|
"memory": [
|
|
387
|
-
(
|
|
387
|
+
(
|
|
388
|
+
"fruxon agents memory list my-agent --subject telegram:123456789",
|
|
389
|
+
"What the agent remembers about one person (participant id or provider:identifier)",
|
|
390
|
+
),
|
|
388
391
|
("fruxon agents memory list my-agent --query 'dark mode'", "Full-text over its memories"),
|
|
389
392
|
("fruxon agents memory subjects my-agent", "Which subjects it has memories about (+ counts)"),
|
|
390
|
-
(
|
|
393
|
+
(
|
|
394
|
+
"fruxon agents memory forget-subject my-agent telegram:123456789 --yes",
|
|
395
|
+
"GDPR-forget everything about a person",
|
|
396
|
+
),
|
|
391
397
|
],
|
|
392
398
|
"metrics": [
|
|
393
399
|
("fruxon metrics list", "Browse evaluation metrics (ids for quality checks)"),
|
|
@@ -15,6 +15,7 @@ import urllib.error
|
|
|
15
15
|
import urllib.parse
|
|
16
16
|
import urllib.request
|
|
17
17
|
import uuid
|
|
18
|
+
import warnings
|
|
18
19
|
from collections.abc import Callable, Iterator
|
|
19
20
|
from datetime import datetime, timezone
|
|
20
21
|
from typing import Any, NoReturn
|
|
@@ -275,6 +276,21 @@ _STATUS_EXCEPTIONS: dict[int, type[FruxonAPIError]] = {
|
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
|
|
279
|
+
# ``chat_user`` on execute / stream / stream_text. The execution body
|
|
280
|
+
# (``AgentExecutionRequestView``) has no field for it — end-user identity is
|
|
281
|
+
# deliberately not settable there — so the server dropped it unread.
|
|
282
|
+
_CHAT_USER_IGNORED = (
|
|
283
|
+
"chat_user is deprecated and has no effect: the agent execution endpoints have no field for it, "
|
|
284
|
+
"so it does not identify the end user to the agent (no per-user memory, no {{sender.*}} values) "
|
|
285
|
+
"and is no longer sent. To give the agent details about the user, pass them in `parameters`."
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _warn_chat_user_ignored() -> None:
|
|
290
|
+
# stacklevel=3: past this helper and the client method, to the caller.
|
|
291
|
+
warnings.warn(_CHAT_USER_IGNORED, DeprecationWarning, stacklevel=3)
|
|
292
|
+
|
|
293
|
+
|
|
278
294
|
def _detect_actor() -> str | None:
|
|
279
295
|
"""Identify the tool making the request, for the ``X-Fruxon-Actor`` header.
|
|
280
296
|
|
|
@@ -376,17 +392,22 @@ class FruxonClient:
|
|
|
376
392
|
agent: The agent identifier.
|
|
377
393
|
parameters: Execution parameters and inputs.
|
|
378
394
|
attachments: File attachments.
|
|
379
|
-
chat_user:
|
|
395
|
+
chat_user: Deprecated, and ignored: it is not sent, and passing it
|
|
396
|
+
emits a :class:`DeprecationWarning`. The execution endpoints
|
|
397
|
+
have no end-user field, so it does not identify the user to
|
|
398
|
+
the agent. Pass what the agent should know about the user in
|
|
399
|
+
``parameters``.
|
|
380
400
|
session_id: Session identifier for multi-turn conversations.
|
|
381
401
|
environment_slug: End-customer environment to attribute this run
|
|
382
402
|
to (per-environment cost tracking, quota enforcement, and
|
|
383
403
|
analytics). Sent as ``environmentSlug`` in the request body.
|
|
384
404
|
"""
|
|
405
|
+
if chat_user is not None:
|
|
406
|
+
_warn_chat_user_ignored()
|
|
385
407
|
url = self._agent_url(agent, ":execute")
|
|
386
408
|
body = self._build_execution_body(
|
|
387
409
|
parameters=parameters,
|
|
388
410
|
attachments=attachments,
|
|
389
|
-
chat_user=chat_user,
|
|
390
411
|
session_id=session_id,
|
|
391
412
|
environment_slug=environment_slug,
|
|
392
413
|
)
|
|
@@ -411,12 +432,16 @@ class FruxonClient:
|
|
|
411
432
|
For the common case of "stream and print the model's text chunks,"
|
|
412
433
|
prefer :meth:`stream_text`, which yields just the concatenable text
|
|
413
434
|
pieces.
|
|
435
|
+
|
|
436
|
+
Arguments are those of :meth:`execute`; ``chat_user`` is deprecated
|
|
437
|
+
and ignored there too.
|
|
414
438
|
"""
|
|
439
|
+
if chat_user is not None:
|
|
440
|
+
_warn_chat_user_ignored()
|
|
415
441
|
url = self._agent_url(agent, ":stream")
|
|
416
442
|
body = self._build_execution_body(
|
|
417
443
|
parameters=parameters,
|
|
418
444
|
attachments=attachments,
|
|
419
|
-
chat_user=chat_user,
|
|
420
445
|
session_id=session_id,
|
|
421
446
|
environment_slug=environment_slug,
|
|
422
447
|
)
|
|
@@ -438,12 +463,17 @@ class FruxonClient:
|
|
|
438
463
|
``chunk`` payload as a string. Use this when you want to mirror the
|
|
439
464
|
chat-style streaming UX without dealing with tool calls, step traces,
|
|
440
465
|
or terminal events.
|
|
466
|
+
|
|
467
|
+
Arguments are those of :meth:`execute`; ``chat_user`` is deprecated
|
|
468
|
+
and ignored there too. Being a generator, it warns on the first
|
|
469
|
+
iteration rather than at the call.
|
|
441
470
|
"""
|
|
471
|
+
if chat_user is not None:
|
|
472
|
+
_warn_chat_user_ignored()
|
|
442
473
|
for event in self.stream(
|
|
443
474
|
agent,
|
|
444
475
|
parameters=parameters,
|
|
445
476
|
attachments=attachments,
|
|
446
|
-
chat_user=chat_user,
|
|
447
477
|
session_id=session_id,
|
|
448
478
|
environment_slug=environment_slug,
|
|
449
479
|
):
|
|
@@ -1406,7 +1436,10 @@ class FruxonClient:
|
|
|
1406
1436
|
``...ListPage`` envelope). The filters answer the question an
|
|
1407
1437
|
author actually has:
|
|
1408
1438
|
|
|
1409
|
-
* ``subject`` — only memories about one person/end-user
|
|
1439
|
+
* ``subject`` — only memories about one person/end-user, named by
|
|
1440
|
+
participant id or by a channel address as ``provider:identifier``
|
|
1441
|
+
(e.g. ``telegram:123456789``); either matches every memory about
|
|
1442
|
+
them;
|
|
1410
1443
|
* ``query`` — full-text over title/content;
|
|
1411
1444
|
* ``scope`` — ``AGENT`` (cross-everyone facts) or ``SUBJECT``
|
|
1412
1445
|
(per-person facts).
|
|
@@ -1450,8 +1483,10 @@ class FruxonClient:
|
|
|
1450
1483
|
"""Forget every memory an agent holds about one subject (GDPR-style).
|
|
1451
1484
|
|
|
1452
1485
|
Hits ``POST .../memories:forgetSubject`` with ``{"subject": …}`` →
|
|
1453
|
-
``{"deletedCount": N}``.
|
|
1454
|
-
|
|
1486
|
+
``{"deletedCount": N}``. ``subject`` is the person's participant id or
|
|
1487
|
+
a channel address as ``provider:identifier``; either forgets the
|
|
1488
|
+
memories filed under the participant id and under each of their
|
|
1489
|
+
addresses. Irreversible — the CLI gates it behind a confirmation.
|
|
1455
1490
|
"""
|
|
1456
1491
|
return self._post_json(self._memories_url(agent, ":forgetSubject"), {"subject": subject})
|
|
1457
1492
|
|
|
@@ -8031,7 +8066,6 @@ class FruxonClient:
|
|
|
8031
8066
|
*,
|
|
8032
8067
|
parameters: dict[str, object] | None,
|
|
8033
8068
|
attachments: list[dict[str, object]] | None,
|
|
8034
|
-
chat_user: dict[str, object] | None,
|
|
8035
8069
|
session_id: str | None,
|
|
8036
8070
|
environment_slug: str | None = None,
|
|
8037
8071
|
) -> dict[str, object]:
|
|
@@ -8040,8 +8074,6 @@ class FruxonClient:
|
|
|
8040
8074
|
body["parameters"] = parameters
|
|
8041
8075
|
if attachments is not None:
|
|
8042
8076
|
body["attachments"] = attachments
|
|
8043
|
-
if chat_user is not None:
|
|
8044
|
-
body["chatUser"] = chat_user
|
|
8045
8077
|
if session_id is not None:
|
|
8046
8078
|
body["sessionId"] = session_id
|
|
8047
8079
|
if environment_slug is not None:
|
|
@@ -5,6 +5,7 @@ import json
|
|
|
5
5
|
import urllib.error
|
|
6
6
|
import urllib.parse
|
|
7
7
|
import urllib.request
|
|
8
|
+
import warnings
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
10
11
|
|
|
@@ -139,7 +140,9 @@ class TestFruxonClientExecute:
|
|
|
139
140
|
client.execute("my-agent")
|
|
140
141
|
assert captured["body"] == {}
|
|
141
142
|
|
|
142
|
-
def
|
|
143
|
+
def test_chat_user_is_deprecated_and_not_sent(self, client, monkeypatch):
|
|
144
|
+
# The execution body has no end-user field: the server dropped
|
|
145
|
+
# ``chatUser`` unread while the caller believed the user was identified.
|
|
143
146
|
captured = {}
|
|
144
147
|
|
|
145
148
|
def mock_urlopen(req, timeout=None, context=None):
|
|
@@ -148,8 +151,22 @@ class TestFruxonClientExecute:
|
|
|
148
151
|
|
|
149
152
|
monkeypatch.setattr(urllib.request, "urlopen", mock_urlopen)
|
|
150
153
|
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
with pytest.warns(DeprecationWarning, match="does not identify the end user") as record:
|
|
155
|
+
client.execute("my-agent", parameters={"q": "hi"}, chat_user={"id": "user-1", "name": "Alice"})
|
|
156
|
+
|
|
157
|
+
assert captured["body"] == {"parameters": {"q": "hi"}}
|
|
158
|
+
assert len(record) == 1
|
|
159
|
+
# Attributed to the caller's line, not to the SDK.
|
|
160
|
+
assert record[0].filename == __file__
|
|
161
|
+
|
|
162
|
+
def test_no_warning_without_chat_user(self, client, monkeypatch):
|
|
163
|
+
monkeypatch.setattr(
|
|
164
|
+
urllib.request, "urlopen", lambda req, timeout=None, context=None: _MockResponse(SAMPLE_RESPONSE)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
with warnings.catch_warnings():
|
|
168
|
+
warnings.simplefilter("error")
|
|
169
|
+
client.execute("my-agent", parameters={"q": "hi"})
|
|
153
170
|
|
|
154
171
|
def test_maps_environment_slug(self, client, monkeypatch):
|
|
155
172
|
captured = {}
|
|
@@ -485,6 +502,24 @@ class TestFruxonClientStream:
|
|
|
485
502
|
chunks = list(client.stream_text("my-agent"))
|
|
486
503
|
assert chunks == ["Hello ", "world"]
|
|
487
504
|
|
|
505
|
+
@pytest.mark.parametrize("method", ["stream", "stream_text"])
|
|
506
|
+
def test_chat_user_is_deprecated_and_not_sent(self, client, monkeypatch, method):
|
|
507
|
+
captured = {}
|
|
508
|
+
|
|
509
|
+
def mock_urlopen(req, timeout=None, context=None):
|
|
510
|
+
captured["body"] = json.loads(req.data.decode("utf-8"))
|
|
511
|
+
return self._mock_stream(self._sse_body(("text", '{"chunk": "x"}'), ("done", "{}")))
|
|
512
|
+
|
|
513
|
+
monkeypatch.setattr(urllib.request, "urlopen", mock_urlopen)
|
|
514
|
+
|
|
515
|
+
with pytest.warns(DeprecationWarning, match="does not identify the end user") as record:
|
|
516
|
+
list(getattr(client, method)("my-agent", parameters={"q": "hi"}, chat_user={"id": "user-1"}))
|
|
517
|
+
|
|
518
|
+
assert captured["body"] == {"parameters": {"q": "hi"}}
|
|
519
|
+
# stream_text rides on stream, but warns once, and from the caller's line.
|
|
520
|
+
assert len(record) == 1
|
|
521
|
+
assert record[0].filename == __file__
|
|
522
|
+
|
|
488
523
|
def test_ignores_sse_comments_and_heartbeats(self, client, monkeypatch):
|
|
489
524
|
# `:` lines are SSE comments — used as keep-alives by some servers.
|
|
490
525
|
# Bare blank lines without a preceding event should also not yield.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|