minutehand 0.0.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.
- minutehand-0.0.1/.gitignore +235 -0
- minutehand-0.0.1/LICENSE.md +105 -0
- minutehand-0.0.1/PKG-INFO +179 -0
- minutehand-0.0.1/README.md +131 -0
- minutehand-0.0.1/examples/external_emulator/agent.py +24 -0
- minutehand-0.0.1/examples/external_emulator/agent.yaml +17 -0
- minutehand-0.0.1/examples/external_emulator/scenario.yaml +7 -0
- minutehand-0.0.1/examples/follow_up/README.md +131 -0
- minutehand-0.0.1/examples/follow_up/agent.py +179 -0
- minutehand-0.0.1/examples/follow_up/agent.yaml +41 -0
- minutehand-0.0.1/examples/follow_up/scenario.yaml +38 -0
- minutehand-0.0.1/examples/follow_up/scenario_silent.yaml +24 -0
- minutehand-0.0.1/examples/recipes/README.md +100 -0
- minutehand-0.0.1/examples/recipes/claude_agent_sdk/README.md +82 -0
- minutehand-0.0.1/examples/recipes/claude_agent_sdk/agent.py +207 -0
- minutehand-0.0.1/examples/recipes/claude_agent_sdk/agent.yaml +21 -0
- minutehand-0.0.1/examples/recipes/claude_agent_sdk/scenario_late.yaml +32 -0
- minutehand-0.0.1/examples/recipes/claude_agent_sdk/scenario_silent.yaml +20 -0
- minutehand-0.0.1/examples/recipes/fake_model.py +318 -0
- minutehand-0.0.1/examples/recipes/langgraph/README.md +71 -0
- minutehand-0.0.1/examples/recipes/langgraph/agent.py +220 -0
- minutehand-0.0.1/examples/recipes/langgraph/agent.yaml +21 -0
- minutehand-0.0.1/examples/recipes/langgraph/scenario_late.yaml +32 -0
- minutehand-0.0.1/examples/recipes/langgraph/scenario_silent.yaml +20 -0
- minutehand-0.0.1/examples/recipes/openai_agents/README.md +78 -0
- minutehand-0.0.1/examples/recipes/openai_agents/agent.py +180 -0
- minutehand-0.0.1/examples/recipes/openai_agents/agent.yaml +21 -0
- minutehand-0.0.1/examples/recipes/openai_agents/scenario_late.yaml +32 -0
- minutehand-0.0.1/examples/recipes/openai_agents/scenario_silent.yaml +20 -0
- minutehand-0.0.1/examples/recipes/pydantic_ai/README.md +85 -0
- minutehand-0.0.1/examples/recipes/pydantic_ai/agent.py +178 -0
- minutehand-0.0.1/examples/recipes/pydantic_ai/agent.yaml +21 -0
- minutehand-0.0.1/examples/recipes/pydantic_ai/scenario_late.yaml +32 -0
- minutehand-0.0.1/examples/recipes/pydantic_ai/scenario_silent.yaml +20 -0
- minutehand-0.0.1/examples/recipes/vercel_ai_sdk/README.md +78 -0
- minutehand-0.0.1/examples/recipes/vercel_ai_sdk/agent.ts +177 -0
- minutehand-0.0.1/examples/recipes/vercel_ai_sdk/agent.yaml +27 -0
- minutehand-0.0.1/examples/recipes/vercel_ai_sdk/package-lock.json +296 -0
- minutehand-0.0.1/examples/recipes/vercel_ai_sdk/package.json +18 -0
- minutehand-0.0.1/examples/recipes/vercel_ai_sdk/scenario_late.yaml +32 -0
- minutehand-0.0.1/examples/recipes/vercel_ai_sdk/scenario_silent.yaml +20 -0
- minutehand-0.0.1/examples/reference_agent/.gitignore +5 -0
- minutehand-0.0.1/examples/reference_agent/agent.yaml +84 -0
- minutehand-0.0.1/examples/reference_agent/api.py +248 -0
- minutehand-0.0.1/examples/reference_agent/hooks.py +136 -0
- minutehand-0.0.1/examples/reference_agent/outside.py +326 -0
- minutehand-0.0.1/examples/reference_agent/run.py +77 -0
- minutehand-0.0.1/examples/reference_agent/scenario.yaml +22 -0
- minutehand-0.0.1/examples/reference_agent/scenario_approved.yaml +29 -0
- minutehand-0.0.1/examples/reference_agent/scenario_approver_away.yaml +30 -0
- minutehand-0.0.1/examples/reference_agent/scenario_approver_silent.yaml +24 -0
- minutehand-0.0.1/examples/reference_agent/scenario_directed.yaml +22 -0
- minutehand-0.0.1/examples/reference_agent/scenario_lenient.yaml +19 -0
- minutehand-0.0.1/examples/reference_agent/scenario_long.yaml +11 -0
- minutehand-0.0.1/examples/reference_agent/scenario_rejected.yaml +28 -0
- minutehand-0.0.1/examples/reference_agent/scenario_silent.yaml +14 -0
- minutehand-0.0.1/examples/reference_agent/store.py +412 -0
- minutehand-0.0.1/examples/reference_agent/telemetry.py +100 -0
- minutehand-0.0.1/examples/reference_agent/worker.py +341 -0
- minutehand-0.0.1/examples/state/README.md +45 -0
- minutehand-0.0.1/examples/state/firestore_emulator/.gitignore +2 -0
- minutehand-0.0.1/examples/state/firestore_emulator/Dockerfile +13 -0
- minutehand-0.0.1/examples/state/firestore_emulator/README.md +76 -0
- minutehand-0.0.1/examples/state/firestore_emulator/agent.py +176 -0
- minutehand-0.0.1/examples/state/firestore_emulator/agent.yaml +24 -0
- minutehand-0.0.1/examples/state/firestore_emulator/compose.yaml +15 -0
- minutehand-0.0.1/examples/state/firestore_emulator/firebase.json +15 -0
- minutehand-0.0.1/examples/state/firestore_emulator/hooks.py +226 -0
- minutehand-0.0.1/examples/state/firestore_emulator/scenario.yaml +16 -0
- minutehand-0.0.1/examples/state/firestore_emulator/start.sh +9 -0
- minutehand-0.0.1/examples/state/postgres/README.md +85 -0
- minutehand-0.0.1/examples/state/postgres/agent.py +195 -0
- minutehand-0.0.1/examples/state/postgres/agent.yaml +30 -0
- minutehand-0.0.1/examples/state/postgres/agent_asyncpg.py +212 -0
- minutehand-0.0.1/examples/state/sqlite/README.md +76 -0
- minutehand-0.0.1/examples/state/sqlite/agent.py +174 -0
- minutehand-0.0.1/examples/state/sqlite/agent.yaml +31 -0
- minutehand-0.0.1/examples/state/sqlite/fork_rosa_answers.yaml +10 -0
- minutehand-0.0.1/examples/state/sqlite/hooks.py +64 -0
- minutehand-0.0.1/examples/state/sqlite/scenario_silent.yaml +24 -0
- minutehand-0.0.1/pyproject.toml +180 -0
- minutehand-0.0.1/src/minutehand/__init__.py +6 -0
- minutehand-0.0.1/src/minutehand/adapters/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/__init__.py +1 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/command.py +54 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/inboxes.py +324 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/openapi.py +224 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/polled.py +37 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/reach.py +55 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/replies.py +138 -0
- minutehand-0.0.1/src/minutehand/adapters/agent/reported.py +85 -0
- minutehand-0.0.1/src/minutehand/adapters/answering.py +251 -0
- minutehand-0.0.1/src/minutehand/adapters/control/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/control/app.py +646 -0
- minutehand-0.0.1/src/minutehand/adapters/control/wire.py +599 -0
- minutehand-0.0.1/src/minutehand/adapters/database/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/database/postgres/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/database/postgres/client.py +258 -0
- minutehand-0.0.1/src/minutehand/adapters/database/postgres/conversation.py +405 -0
- minutehand-0.0.1/src/minutehand/adapters/database/postgres/relay.py +447 -0
- minutehand-0.0.1/src/minutehand/adapters/database/postgres/wire.py +168 -0
- minutehand-0.0.1/src/minutehand/adapters/emulator/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/emulator/answers.py +58 -0
- minutehand-0.0.1/src/minutehand/adapters/emulator/fleet.py +77 -0
- minutehand-0.0.1/src/minutehand/adapters/emulator/process.py +254 -0
- minutehand-0.0.1/src/minutehand/adapters/emulator/relay.py +208 -0
- minutehand-0.0.1/src/minutehand/adapters/mcp/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/mcp/results.py +193 -0
- minutehand-0.0.1/src/minutehand/adapters/mcp/server.py +387 -0
- minutehand-0.0.1/src/minutehand/adapters/model/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/model/openai_compatible.py +222 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/CLAIMS.md +65 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/__init__.py +5 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/app.py +1284 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/manifest.py +27 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/provider.py +164 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/seed.py +596 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/state.py +451 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/asana/wire.py +1299 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/aws/__init__.py +1 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/aws/manifest.py +28 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/aws/provider.py +418 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/aws/schedule.py +134 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/aws/wire.py +386 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/CLAIMS.md +97 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/README.md +57 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/__init__.py +5 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/app.py +793 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/content.py +161 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/graphql.py +826 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/manifest.py +16 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/provider.py +51 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/search.py +169 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/seed.py +398 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/state.py +169 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/github/wire.py +631 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_cloud_tasks/CLAIMS.md +40 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_cloud_tasks/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_cloud_tasks/manifest.py +21 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_cloud_tasks/provider.py +701 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_cloud_tasks/wire.py +224 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/CLAIMS.md +154 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/__init__.py +5 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/access.py +53 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/app.py +1742 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/calendar_wire.py +283 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/calendars.py +734 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/docs.py +1761 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/gmail.py +690 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/gmail_query.py +239 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/gmail_wire.py +338 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/manifest.py +34 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/provider.py +83 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/query.py +405 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/seed.py +399 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/slides.py +1252 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/state.py +632 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/google_workspace/wire.py +1015 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/CLAIMS.md +70 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/README.md +79 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/__init__.py +2 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/app.py +1398 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/jql.py +329 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/manifest.py +22 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/moves.py +443 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/provider.py +220 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/search.py +540 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/seed.py +675 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/state.py +441 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/jira/wire.py +1027 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/CLAIMS.md +128 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/README.md +156 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/__init__.py +8 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/app.py +372 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/cards.py +143 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/common.py +103 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/connector.py +432 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/docx.py +64 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/graph_calendar.py +707 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/graph_files.py +1105 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/graph_mail.py +710 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/graph_teams.py +496 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/inbound.py +577 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/keys.py +106 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/manifest.py +36 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/provider.py +283 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/seed.py +715 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/signin.py +446 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/state.py +733 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/subscriptions.py +250 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/tokens.py +138 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/microsoft/wire.py +1200 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/CLAIMS.md +27 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/README.md +130 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/__init__.py +1 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/app.py +943 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/edits.py +496 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/manifest.py +21 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/provider.py +184 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/query.py +382 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/seed.py +654 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/state.py +529 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/webhooks.py +118 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/notion/wire.py +1356 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/CLAIMS.md +70 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/README.md +93 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/__init__.py +8 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/app.py +990 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/inbound.py +533 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/interactive.py +422 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/manifest.py +15 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/provider.py +122 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/seed.py +500 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/socket_mode.py +208 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/state.py +566 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/slack/wire.py +1381 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/CLAIMS.md +105 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/__init__.py +5 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/access.py +100 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/activities.py +257 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/app.py +1005 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/fields.py +191 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/hub.py +224 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/manifest.py +22 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/present.py +290 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/provider.py +203 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/query.py +365 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/search.py +339 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/seed.py +640 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/state.py +607 -0
- minutehand-0.0.1/src/minutehand/adapters/providers/youtrack/wire.py +1164 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/addon.py +1450 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/base_url.py +168 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/capture.py +683 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/connect.py +66 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/credentials.py +129 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/edit.py +117 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/hosts.py +61 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/local.py +201 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/mcp.py +77 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/model_calls.py +372 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/modeled.py +68 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/policy.py +118 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/redact.py +109 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/redirected.py +167 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/registry.py +115 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/server.py +182 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/trust.py +42 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/tunnel.py +134 -0
- minutehand-0.0.1/src/minutehand/adapters/proxy/worlds.py +129 -0
- minutehand-0.0.1/src/minutehand/adapters/store/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/store/sqlite.py +1076 -0
- minutehand-0.0.1/src/minutehand/adapters/telemetry/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/telemetry/forward.py +105 -0
- minutehand-0.0.1/src/minutehand/adapters/telemetry/otel.py +401 -0
- minutehand-0.0.1/src/minutehand/adapters/telemetry/otlp.py +285 -0
- minutehand-0.0.1/src/minutehand/adapters/telemetry/receiver.py +403 -0
- minutehand-0.0.1/src/minutehand/adapters/web/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/adapters/web/app.py +515 -0
- minutehand-0.0.1/src/minutehand/adapters/web/responses.py +253 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/d3-7.9.0/LICENSE.txt +13 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/d3-7.9.0/d3.min.js +2 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/plot-0.6.17/LICENSE.txt +13 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/plot-0.6.17/plot.umd.min.js +2 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/vis-timeline-8.5.4/LICENSE.Apache-2.0.txt +176 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/vis-timeline-8.5.4/LICENSE.MIT.txt +22 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/vis-timeline-8.5.4/THIRD-PARTY.txt +235 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/vis-timeline-8.5.4/vis-timeline-graph2d.min.css +2 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/vendor/vis-timeline-8.5.4/vis-timeline-graph2d.min.js +50 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/viewer.css +260 -0
- minutehand-0.0.1/src/minutehand/adapters/web/static/viewer.js +1359 -0
- minutehand-0.0.1/src/minutehand/adapters/web/viewer.html +24 -0
- minutehand-0.0.1/src/minutehand/agent_api.py +122 -0
- minutehand-0.0.1/src/minutehand/application/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/application/around_proxy.py +119 -0
- minutehand-0.0.1/src/minutehand/application/cases.py +253 -0
- minutehand-0.0.1/src/minutehand/application/checkpoint.py +198 -0
- minutehand-0.0.1/src/minutehand/application/databases.py +294 -0
- minutehand-0.0.1/src/minutehand/application/dues.py +235 -0
- minutehand-0.0.1/src/minutehand/application/emulators.py +122 -0
- minutehand-0.0.1/src/minutehand/application/files.py +193 -0
- minutehand-0.0.1/src/minutehand/application/forks.py +684 -0
- minutehand-0.0.1/src/minutehand/application/further_seed.py +193 -0
- minutehand-0.0.1/src/minutehand/application/inboxes.py +252 -0
- minutehand-0.0.1/src/minutehand/application/library.py +87 -0
- minutehand-0.0.1/src/minutehand/application/machine.py +61 -0
- minutehand-0.0.1/src/minutehand/application/model_calls.py +265 -0
- minutehand-0.0.1/src/minutehand/application/orchestrator.py +1252 -0
- minutehand-0.0.1/src/minutehand/application/outbound.py +166 -0
- minutehand-0.0.1/src/minutehand/application/refusals.py +49 -0
- minutehand-0.0.1/src/minutehand/application/replier_model.py +359 -0
- minutehand-0.0.1/src/minutehand/application/replier_scripted.py +214 -0
- minutehand-0.0.1/src/minutehand/application/restore.py +543 -0
- minutehand-0.0.1/src/minutehand/application/rewind.py +462 -0
- minutehand-0.0.1/src/minutehand/application/run_clock.py +41 -0
- minutehand-0.0.1/src/minutehand/application/sandbox.py +105 -0
- minutehand-0.0.1/src/minutehand/application/standing.py +824 -0
- minutehand-0.0.1/src/minutehand/application/state_hooks.py +87 -0
- minutehand-0.0.1/src/minutehand/application/steps.py +188 -0
- minutehand-0.0.1/src/minutehand/application/watching.py +73 -0
- minutehand-0.0.1/src/minutehand/checks/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/checks/_waits.py +289 -0
- minutehand-0.0.1/src/minutehand/checks/acted_after_deadline.py +45 -0
- minutehand-0.0.1/src/minutehand/checks/acted_on_repeated_wake.py +86 -0
- minutehand-0.0.1/src/minutehand/checks/acted_without_approval.py +96 -0
- minutehand-0.0.1/src/minutehand/checks/agent_contract_changed.py +37 -0
- minutehand-0.0.1/src/minutehand/checks/around_proxy.py +58 -0
- minutehand-0.0.1/src/minutehand/checks/chased_absent_person.py +39 -0
- minutehand-0.0.1/src/minutehand/checks/duplicate_ticket.py +56 -0
- minutehand-0.0.1/src/minutehand/checks/effectiveness.py +137 -0
- minutehand-0.0.1/src/minutehand/checks/expectations.py +338 -0
- minutehand-0.0.1/src/minutehand/checks/idle_wake.py +57 -0
- minutehand-0.0.1/src/minutehand/checks/judged/__init__.py +1 -0
- minutehand-0.0.1/src/minutehand/checks/judged/asked_about.py +101 -0
- minutehand-0.0.1/src/minutehand/checks/judged/ticket_is_actionable.py +108 -0
- minutehand-0.0.1/src/minutehand/checks/kept_chasing_after_done.py +64 -0
- minutehand-0.0.1/src/minutehand/checks/late_follow_up.py +55 -0
- minutehand-0.0.1/src/minutehand/checks/ledger.py +346 -0
- minutehand-0.0.1/src/minutehand/checks/nagged.py +82 -0
- minutehand-0.0.1/src/minutehand/checks/near_miss_name.py +66 -0
- minutehand-0.0.1/src/minutehand/checks/no_follow_up.py +73 -0
- minutehand-0.0.1/src/minutehand/checks/patterns.py +100 -0
- minutehand-0.0.1/src/minutehand/checks/planned_past_due.py +83 -0
- minutehand-0.0.1/src/minutehand/checks/repeated_message.py +85 -0
- minutehand-0.0.1/src/minutehand/checks/reported_against_world.py +157 -0
- minutehand-0.0.1/src/minutehand/checks/runner.py +498 -0
- minutehand-0.0.1/src/minutehand/checks/slow_to_react.py +53 -0
- minutehand-0.0.1/src/minutehand/checks/unmatched_call.py +43 -0
- minutehand-0.0.1/src/minutehand/cli.py +1091 -0
- minutehand-0.0.1/src/minutehand/doctor.py +398 -0
- minutehand-0.0.1/src/minutehand/domain/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/domain/absence.py +41 -0
- minutehand-0.0.1/src/minutehand/domain/agent.py +356 -0
- minutehand-0.0.1/src/minutehand/domain/checks.py +284 -0
- minutehand-0.0.1/src/minutehand/domain/clock.py +116 -0
- minutehand-0.0.1/src/minutehand/domain/conversation.py +40 -0
- minutehand-0.0.1/src/minutehand/domain/database.py +238 -0
- minutehand-0.0.1/src/minutehand/domain/emulator.py +189 -0
- minutehand-0.0.1/src/minutehand/domain/errors.py +66 -0
- minutehand-0.0.1/src/minutehand/domain/experiment.py +80 -0
- minutehand-0.0.1/src/minutehand/domain/inboxes.py +365 -0
- minutehand-0.0.1/src/minutehand/domain/jsonpath.py +186 -0
- minutehand-0.0.1/src/minutehand/domain/library.py +154 -0
- minutehand-0.0.1/src/minutehand/domain/outbound.py +335 -0
- minutehand-0.0.1/src/minutehand/domain/people.py +139 -0
- minutehand-0.0.1/src/minutehand/domain/provider.py +224 -0
- minutehand-0.0.1/src/minutehand/domain/run.py +144 -0
- minutehand-0.0.1/src/minutehand/domain/scenario.py +1141 -0
- minutehand-0.0.1/src/minutehand/domain/storage.py +43 -0
- minutehand-0.0.1/src/minutehand/domain/telemetry.py +144 -0
- minutehand-0.0.1/src/minutehand/domain/templates.py +65 -0
- minutehand-0.0.1/src/minutehand/domain/world.py +544 -0
- minutehand-0.0.1/src/minutehand/library/approval_rejected.yaml +40 -0
- minutehand-0.0.1/src/minutehand/library/approver_never_decides.yaml +38 -0
- minutehand-0.0.1/src/minutehand/library/deadline_moves_earlier.yaml +33 -0
- minutehand-0.0.1/src/minutehand/library/person_answers_late.yaml +30 -0
- minutehand-0.0.1/src/minutehand/library/person_answers_when_reminded.yaml +30 -0
- minutehand-0.0.1/src/minutehand/library/person_away_with_delegate.yaml +44 -0
- minutehand-0.0.1/src/minutehand/library/person_goes_quiet.yaml +26 -0
- minutehand-0.0.1/src/minutehand/library/planned_wake_dropped.yaml +27 -0
- minutehand-0.0.1/src/minutehand/library/planned_wake_late.yaml +28 -0
- minutehand-0.0.1/src/minutehand/library/planned_wake_twice.yaml +32 -0
- minutehand-0.0.1/src/minutehand/library/someone_else_writes_while_waiting.yaml +41 -0
- minutehand-0.0.1/src/minutehand/mcp_relay.py +75 -0
- minutehand-0.0.1/src/minutehand/ports/__init__.py +0 -0
- minutehand-0.0.1/src/minutehand/ports/agent.py +41 -0
- minutehand-0.0.1/src/minutehand/ports/clock.py +16 -0
- minutehand-0.0.1/src/minutehand/ports/database.py +52 -0
- minutehand-0.0.1/src/minutehand/ports/inboxes.py +32 -0
- minutehand-0.0.1/src/minutehand/ports/model.py +60 -0
- minutehand-0.0.1/src/minutehand/ports/people.py +21 -0
- minutehand-0.0.1/src/minutehand/ports/provider.py +365 -0
- minutehand-0.0.1/src/minutehand/ports/store.py +168 -0
- minutehand-0.0.1/src/minutehand/ports/telemetry.py +37 -0
- minutehand-0.0.1/src/minutehand/py.typed +0 -0
- minutehand-0.0.1/src/minutehand/serve.py +1192 -0
- minutehand-0.0.1/src/minutehand/session.py +1728 -0
- minutehand-0.0.1/src/minutehand/testing/__init__.py +10 -0
- minutehand-0.0.1/src/minutehand/testing/background.py +50 -0
- minutehand-0.0.1/src/minutehand/testing/client.py +542 -0
- minutehand-0.0.1/src/minutehand/testing/plugin.py +59 -0
- minutehand-0.0.1/src/minutehand/testing/world.py +463 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# uv
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs/Zensical documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
|
|
201
|
+
# can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer, you
|
|
203
|
+
# could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
|
221
|
+
|
|
222
|
+
# Minutehand run state
|
|
223
|
+
.state*/
|
|
224
|
+
*.db
|
|
225
|
+
*.db-wal
|
|
226
|
+
*.db-shm
|
|
227
|
+
|
|
228
|
+
# A run kept by `minutehand run` from the folder it was started in
|
|
229
|
+
.minutehand/
|
|
230
|
+
|
|
231
|
+
# Throwaway files of a working session, never committed
|
|
232
|
+
.scratch/
|
|
233
|
+
|
|
234
|
+
# The Node recipe's packages, installed by npm ci in its folder
|
|
235
|
+
node_modules/
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, ALv2 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-ALv2
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Alknoma
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
91
|
+
the date we make the Software available. On or after that date, you may use the
|
|
92
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
93
|
+
will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
96
|
+
this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
103
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
|
+
specific language governing permissions and limitations under the License.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: minutehand
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Build your proactive agent yourself: simulated days, fake services, real findings.
|
|
5
|
+
Project-URL: Repository, https://github.com/Alknoma/minutehand
|
|
6
|
+
Project-URL: Issues, https://github.com/Alknoma/minutehand/issues
|
|
7
|
+
Project-URL: Documentation, https://github.com/Alknoma/minutehand/tree/integration-main/docs
|
|
8
|
+
Author: Alknoma
|
|
9
|
+
License-Expression: FSL-1.1-ALv2
|
|
10
|
+
License-File: LICENSE.md
|
|
11
|
+
Keywords: agents,ai-agents,evaluation,fakes,proxy,pytest,simulation,testing
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Framework :: Pytest
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Testing
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing :: Mocking
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Requires-Dist: asgiref>=3.8
|
|
27
|
+
Requires-Dist: certifi>=2024.2.2
|
|
28
|
+
Requires-Dist: flask>=3
|
|
29
|
+
Requires-Dist: httpx>=0.27
|
|
30
|
+
Requires-Dist: jsonschema>=4.20
|
|
31
|
+
Requires-Dist: mcp<2,>=1.2
|
|
32
|
+
Requires-Dist: mitmproxy<13,>=12
|
|
33
|
+
Requires-Dist: moto[server]>=5.2
|
|
34
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27
|
|
35
|
+
Requires-Dist: opentelemetry-proto>=1.27
|
|
36
|
+
Requires-Dist: opentelemetry-sdk>=1.27
|
|
37
|
+
Requires-Dist: protobuf>=5
|
|
38
|
+
Requires-Dist: pydantic>=2.9
|
|
39
|
+
Requires-Dist: pyyaml>=6
|
|
40
|
+
Requires-Dist: starlette>=0.40
|
|
41
|
+
Requires-Dist: uvicorn>=0.30
|
|
42
|
+
Requires-Dist: wsproto>=1.2
|
|
43
|
+
Requires-Dist: zstandard>=0.23
|
|
44
|
+
Provides-Extra: grpc
|
|
45
|
+
Requires-Dist: google-cloud-tasks>=2.16; extra == 'grpc'
|
|
46
|
+
Requires-Dist: grpcio>=1.60; extra == 'grpc'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# Minutehand
|
|
50
|
+
|
|
51
|
+
Minutehand runs your proactive agent through simulated days of work in a few seconds and tells you how well it
|
|
52
|
+
carried the work. The agent talks to fake Slack, Teams, Asana, Jira, YouTrack, Notion, GitHub, Google Drive and others,
|
|
53
|
+
with people who answer late or not at all. Minutehand owns the clock, records every change in an append-only
|
|
54
|
+
log, and runs checks on the result. Each failure names the design that fixes it. A finished run can be forked
|
|
55
|
+
from a checkpoint with the prompt, the model, a person or the world changed, and played forward again.
|
|
56
|
+
|
|
57
|
+
The agent's code does not change. Minutehand starts the agent's own command, or reaches one already running,
|
|
58
|
+
and points it at the fakes through its environment: `HTTPS_PROXY`, `NO_PROXY` and a CA bundle.
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
You need [uv](https://docs.astral.sh/uv/). It fetches Python 3.12 if you do not have it.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv tool install git+https://github.com/Alknoma/minutehand@integration-main
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This puts the `minutehand` command in an environment of its own, apart from your agent's dependencies. Minutehand
|
|
69
|
+
is not published on PyPI.
|
|
70
|
+
|
|
71
|
+
## Quick start
|
|
72
|
+
|
|
73
|
+
The example agent is in the repository, so clone it for the example files:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone --depth 1 -b integration-main https://github.com/Alknoma/minutehand
|
|
77
|
+
cd minutehand/examples/follow_up
|
|
78
|
+
python3 -m venv .venv && .venv/bin/pip install slack_sdk # the agent's one library, in the agent's own Python
|
|
79
|
+
|
|
80
|
+
minutehand run scenario.yaml --agent agent.yaml -- .venv/bin/python agent.py
|
|
81
|
+
# exits 0: Rosa answers after a day and a half, and the agent tells Owen and finishes
|
|
82
|
+
|
|
83
|
+
AGENT_BEHAVIOUR=forgetful minutehand run scenario_silent.yaml --agent agent.yaml -- .venv/bin/python agent.py
|
|
84
|
+
# exits 1: Rosa never answers, the agent never follows up, and no_follow_up names the fix
|
|
85
|
+
|
|
86
|
+
minutehand runs # every run, one line each
|
|
87
|
+
minutehand findings <run_id> # a run's findings again, and the checkpoints it can be forked from
|
|
88
|
+
minutehand view # the runs in a browser, at http://127.0.0.1:8081/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Each run takes a few seconds. Runs are kept in `.minutehand/` in the folder you ran them from. The example's
|
|
92
|
+
`README.md` explains both runs line by line.
|
|
93
|
+
|
|
94
|
+
To try your own agent, write an agent file (`minutehand schema agent` prints its JSON Schema) and a scenario,
|
|
95
|
+
check them with `minutehand validate`, and run `minutehand doctor -- <your agent's command>` to find any HTTP
|
|
96
|
+
client in the agent that would go around the proxy.
|
|
97
|
+
|
|
98
|
+
## How the agent touches Minutehand
|
|
99
|
+
|
|
100
|
+
Almost nothing is required. An agent that takes its goal by message and books its own wakes implements no
|
|
101
|
+
endpoint. An agent can also take a wake (a `POST` carrying the simulated `now`), answer a report (still working,
|
|
102
|
+
done, when to wake next), take pushed events in each provider's own format, and list its own inboxes for the
|
|
103
|
+
simulated people to decide. Hosts no fake answers are declared in the agent file: acknowledge, pass through,
|
|
104
|
+
replay, or forward to an emulator of your own. `docs/agent-contract.md` lists every touch point, and
|
|
105
|
+
`schemas/agent-api.openapi.json` describes the endpoints.
|
|
106
|
+
|
|
107
|
+
## Status
|
|
108
|
+
|
|
109
|
+
Built and tested (`docs/design.md`, "What exists", counts the tests for each part):
|
|
110
|
+
|
|
111
|
+
- The proxy, the run loop, the store (SQLite, one file per run and its forks), forks from a checkpoint with the
|
|
112
|
+
agent's own state restored and verified, and `minutehand serve` for test suites that open many worlds at once.
|
|
113
|
+
- Providers: Slack, Microsoft Teams and Graph, Asana, Jira, YouTrack, Notion, GitHub, Google Drive with Docs and Slides,
|
|
114
|
+
AWS EventBridge Scheduler and SQS (through moto), and Google Cloud Tasks over its REST transport.
|
|
115
|
+
- 19 checks, the scorecard and 10 patterns. Among them, `planned_past_due` flags a follow-up that was on time
|
|
116
|
+
only because something other than the agent's own plan woke it; the loop's table of what was due is recorded
|
|
117
|
+
to answer that. `reported_against_world` holds what the agent says it is waiting on against what the world
|
|
118
|
+
shows.
|
|
119
|
+
- Checks of the agent's own, kept beside its agent file (`checks:`) and run with Minutehand's.
|
|
120
|
+
- Dispatch rules: a scenario can deliver the agent's own wakes late, twice or not at all, and a fork can change
|
|
121
|
+
them (`DispatchChange`).
|
|
122
|
+
- Outbound capture, with `--capture-unknown` for a first run: `reads` passes only GET, HEAD and OPTIONS, and
|
|
123
|
+
`model` lets a model stand in for a service nobody declared once the agent writes to it.
|
|
124
|
+
- The agent's machine: `machine:` commands in a scenario change files at a moment, and the folders an agent file
|
|
125
|
+
`watches:` are recorded as they change. The `file_removed` expectation reads them.
|
|
126
|
+
- MCP: an agent's tool calls are recorded over HTTP through the proxy, and over standard input and output with
|
|
127
|
+
`minutehand mcp-relay`. The `tool_called` expectation reads them. `minutehand mcp` serves the tools a coding
|
|
128
|
+
agent uses to run scenarios and read findings.
|
|
129
|
+
- The agent's own OpenTelemetry received and joined to the world events it caused; telemetry out over OTLP.
|
|
130
|
+
- The run viewer (`minutehand view`), `doctor`, `validate` and `schema`, and a container image (`Dockerfile`)
|
|
131
|
+
that serves by default.
|
|
132
|
+
|
|
133
|
+
Experimental: `Contained`, an agent in a gVisor sandbox whose clock Minutehand owns, so timers in the agent's own
|
|
134
|
+
process become its wakes with no code change. It needs a patched gVisor that is kept outside this repository,
|
|
135
|
+
and has been run on arm64 only.
|
|
136
|
+
|
|
137
|
+
Checked by hand, not in the test suite: an agent in containers (`docs/containers.md`).
|
|
138
|
+
|
|
139
|
+
Not built: people written by a model, checks a model judges, generated providers, a faked system clock
|
|
140
|
+
(`libfaketime`), the hosted service. No fake has been checked against the real service's wire details; each is
|
|
141
|
+
tested against the service's own client library. `docs/design.md`, "Known issues", lists the limits of what is
|
|
142
|
+
built.
|
|
143
|
+
|
|
144
|
+
## Docs
|
|
145
|
+
|
|
146
|
+
| Page | What it covers |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `docs/design.md` | The design, what exists, and its known limits |
|
|
149
|
+
| `docs/agent-contract.md` | Every way an agent and Minutehand touch |
|
|
150
|
+
| `docs/capture.md` | Hosts no fake answers: acknowledge, pass through, replay, `--capture-unknown` |
|
|
151
|
+
| `docs/containers.md` | An agent in a container, and the Docker `NO_PROXY` trap |
|
|
152
|
+
| `docs/serve.md` | `minutehand serve`, for a test suite |
|
|
153
|
+
| `docs/external-emulators.md` | Forwarding a host to a fake of your own |
|
|
154
|
+
| `docs/inboxes.md` | Work that waits on a person in the agent's own product |
|
|
155
|
+
| `docs/reference-agent.md` | A larger example: two processes, a job queue, email, a model API |
|
|
156
|
+
|
|
157
|
+
## Scenarios to start from
|
|
158
|
+
|
|
159
|
+
`minutehand scenarios` lists a library of ready-made situations (a person goes quiet, answers late, is away with a
|
|
160
|
+
delegate; an approval is rejected or never decided; a deadline moves; a scheduled wake comes late, twice or never).
|
|
161
|
+
`minutehand scenarios new --all --goal ... --owner 'Name <email>' --ask 'Name <email>'` writes each out with your
|
|
162
|
+
values. See `docs/scenarios.md`.
|
|
163
|
+
|
|
164
|
+
## Develop
|
|
165
|
+
|
|
166
|
+
From a checkout:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
uv sync
|
|
170
|
+
uv run pytest -q -n auto
|
|
171
|
+
uv run pyright
|
|
172
|
+
uv run python -m lints
|
|
173
|
+
uv run ruff check . && uv run ruff format --check .
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`CLAUDE.md` is the house rules, `CONTRIBUTING.md` how to contribute, and `docs/lints.md` argues each lint.
|
|
177
|
+
`uv tool install .` installs the checkout's own `minutehand`.
|
|
178
|
+
|
|
179
|
+
Licensed under the Functional Source License 1.1 (Apache-2.0 future licence); see `LICENSE.md`.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Minutehand
|
|
2
|
+
|
|
3
|
+
Minutehand runs your proactive agent through simulated days of work in a few seconds and tells you how well it
|
|
4
|
+
carried the work. The agent talks to fake Slack, Teams, Asana, Jira, YouTrack, Notion, GitHub, Google Drive and others,
|
|
5
|
+
with people who answer late or not at all. Minutehand owns the clock, records every change in an append-only
|
|
6
|
+
log, and runs checks on the result. Each failure names the design that fixes it. A finished run can be forked
|
|
7
|
+
from a checkpoint with the prompt, the model, a person or the world changed, and played forward again.
|
|
8
|
+
|
|
9
|
+
The agent's code does not change. Minutehand starts the agent's own command, or reaches one already running,
|
|
10
|
+
and points it at the fakes through its environment: `HTTPS_PROXY`, `NO_PROXY` and a CA bundle.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
You need [uv](https://docs.astral.sh/uv/). It fetches Python 3.12 if you do not have it.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv tool install git+https://github.com/Alknoma/minutehand@integration-main
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This puts the `minutehand` command in an environment of its own, apart from your agent's dependencies. Minutehand
|
|
21
|
+
is not published on PyPI.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
The example agent is in the repository, so clone it for the example files:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone --depth 1 -b integration-main https://github.com/Alknoma/minutehand
|
|
29
|
+
cd minutehand/examples/follow_up
|
|
30
|
+
python3 -m venv .venv && .venv/bin/pip install slack_sdk # the agent's one library, in the agent's own Python
|
|
31
|
+
|
|
32
|
+
minutehand run scenario.yaml --agent agent.yaml -- .venv/bin/python agent.py
|
|
33
|
+
# exits 0: Rosa answers after a day and a half, and the agent tells Owen and finishes
|
|
34
|
+
|
|
35
|
+
AGENT_BEHAVIOUR=forgetful minutehand run scenario_silent.yaml --agent agent.yaml -- .venv/bin/python agent.py
|
|
36
|
+
# exits 1: Rosa never answers, the agent never follows up, and no_follow_up names the fix
|
|
37
|
+
|
|
38
|
+
minutehand runs # every run, one line each
|
|
39
|
+
minutehand findings <run_id> # a run's findings again, and the checkpoints it can be forked from
|
|
40
|
+
minutehand view # the runs in a browser, at http://127.0.0.1:8081/
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Each run takes a few seconds. Runs are kept in `.minutehand/` in the folder you ran them from. The example's
|
|
44
|
+
`README.md` explains both runs line by line.
|
|
45
|
+
|
|
46
|
+
To try your own agent, write an agent file (`minutehand schema agent` prints its JSON Schema) and a scenario,
|
|
47
|
+
check them with `minutehand validate`, and run `minutehand doctor -- <your agent's command>` to find any HTTP
|
|
48
|
+
client in the agent that would go around the proxy.
|
|
49
|
+
|
|
50
|
+
## How the agent touches Minutehand
|
|
51
|
+
|
|
52
|
+
Almost nothing is required. An agent that takes its goal by message and books its own wakes implements no
|
|
53
|
+
endpoint. An agent can also take a wake (a `POST` carrying the simulated `now`), answer a report (still working,
|
|
54
|
+
done, when to wake next), take pushed events in each provider's own format, and list its own inboxes for the
|
|
55
|
+
simulated people to decide. Hosts no fake answers are declared in the agent file: acknowledge, pass through,
|
|
56
|
+
replay, or forward to an emulator of your own. `docs/agent-contract.md` lists every touch point, and
|
|
57
|
+
`schemas/agent-api.openapi.json` describes the endpoints.
|
|
58
|
+
|
|
59
|
+
## Status
|
|
60
|
+
|
|
61
|
+
Built and tested (`docs/design.md`, "What exists", counts the tests for each part):
|
|
62
|
+
|
|
63
|
+
- The proxy, the run loop, the store (SQLite, one file per run and its forks), forks from a checkpoint with the
|
|
64
|
+
agent's own state restored and verified, and `minutehand serve` for test suites that open many worlds at once.
|
|
65
|
+
- Providers: Slack, Microsoft Teams and Graph, Asana, Jira, YouTrack, Notion, GitHub, Google Drive with Docs and Slides,
|
|
66
|
+
AWS EventBridge Scheduler and SQS (through moto), and Google Cloud Tasks over its REST transport.
|
|
67
|
+
- 19 checks, the scorecard and 10 patterns. Among them, `planned_past_due` flags a follow-up that was on time
|
|
68
|
+
only because something other than the agent's own plan woke it; the loop's table of what was due is recorded
|
|
69
|
+
to answer that. `reported_against_world` holds what the agent says it is waiting on against what the world
|
|
70
|
+
shows.
|
|
71
|
+
- Checks of the agent's own, kept beside its agent file (`checks:`) and run with Minutehand's.
|
|
72
|
+
- Dispatch rules: a scenario can deliver the agent's own wakes late, twice or not at all, and a fork can change
|
|
73
|
+
them (`DispatchChange`).
|
|
74
|
+
- Outbound capture, with `--capture-unknown` for a first run: `reads` passes only GET, HEAD and OPTIONS, and
|
|
75
|
+
`model` lets a model stand in for a service nobody declared once the agent writes to it.
|
|
76
|
+
- The agent's machine: `machine:` commands in a scenario change files at a moment, and the folders an agent file
|
|
77
|
+
`watches:` are recorded as they change. The `file_removed` expectation reads them.
|
|
78
|
+
- MCP: an agent's tool calls are recorded over HTTP through the proxy, and over standard input and output with
|
|
79
|
+
`minutehand mcp-relay`. The `tool_called` expectation reads them. `minutehand mcp` serves the tools a coding
|
|
80
|
+
agent uses to run scenarios and read findings.
|
|
81
|
+
- The agent's own OpenTelemetry received and joined to the world events it caused; telemetry out over OTLP.
|
|
82
|
+
- The run viewer (`minutehand view`), `doctor`, `validate` and `schema`, and a container image (`Dockerfile`)
|
|
83
|
+
that serves by default.
|
|
84
|
+
|
|
85
|
+
Experimental: `Contained`, an agent in a gVisor sandbox whose clock Minutehand owns, so timers in the agent's own
|
|
86
|
+
process become its wakes with no code change. It needs a patched gVisor that is kept outside this repository,
|
|
87
|
+
and has been run on arm64 only.
|
|
88
|
+
|
|
89
|
+
Checked by hand, not in the test suite: an agent in containers (`docs/containers.md`).
|
|
90
|
+
|
|
91
|
+
Not built: people written by a model, checks a model judges, generated providers, a faked system clock
|
|
92
|
+
(`libfaketime`), the hosted service. No fake has been checked against the real service's wire details; each is
|
|
93
|
+
tested against the service's own client library. `docs/design.md`, "Known issues", lists the limits of what is
|
|
94
|
+
built.
|
|
95
|
+
|
|
96
|
+
## Docs
|
|
97
|
+
|
|
98
|
+
| Page | What it covers |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `docs/design.md` | The design, what exists, and its known limits |
|
|
101
|
+
| `docs/agent-contract.md` | Every way an agent and Minutehand touch |
|
|
102
|
+
| `docs/capture.md` | Hosts no fake answers: acknowledge, pass through, replay, `--capture-unknown` |
|
|
103
|
+
| `docs/containers.md` | An agent in a container, and the Docker `NO_PROXY` trap |
|
|
104
|
+
| `docs/serve.md` | `minutehand serve`, for a test suite |
|
|
105
|
+
| `docs/external-emulators.md` | Forwarding a host to a fake of your own |
|
|
106
|
+
| `docs/inboxes.md` | Work that waits on a person in the agent's own product |
|
|
107
|
+
| `docs/reference-agent.md` | A larger example: two processes, a job queue, email, a model API |
|
|
108
|
+
|
|
109
|
+
## Scenarios to start from
|
|
110
|
+
|
|
111
|
+
`minutehand scenarios` lists a library of ready-made situations (a person goes quiet, answers late, is away with a
|
|
112
|
+
delegate; an approval is rejected or never decided; a deadline moves; a scheduled wake comes late, twice or never).
|
|
113
|
+
`minutehand scenarios new --all --goal ... --owner 'Name <email>' --ask 'Name <email>'` writes each out with your
|
|
114
|
+
values. See `docs/scenarios.md`.
|
|
115
|
+
|
|
116
|
+
## Develop
|
|
117
|
+
|
|
118
|
+
From a checkout:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv sync
|
|
122
|
+
uv run pytest -q -n auto
|
|
123
|
+
uv run pyright
|
|
124
|
+
uv run python -m lints
|
|
125
|
+
uv run ruff check . && uv run ruff format --check .
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`CLAUDE.md` is the house rules, `CONTRIBUTING.md` how to contribute, and `docs/lints.md` argues each lint.
|
|
129
|
+
`uv tool install .` installs the checkout's own `minutehand`.
|
|
130
|
+
|
|
131
|
+
Licensed under the Functional Source License 1.1 (Apache-2.0 future licence); see `LICENSE.md`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""A ten-line stand-in for an agent: each wake it files a customer with the payments API and asks for its list,
|
|
2
|
+
through whatever proxy its environment names. It knows nothing of Minutehand or of the emulator answering it."""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
import urllib.error
|
|
9
|
+
import urllib.request
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
|
|
12
|
+
wake = json.load(sys.stdin)
|
|
13
|
+
now = datetime.fromisoformat(wake["now"])
|
|
14
|
+
time.sleep(float(os.environ.get("EXAMPLE_PAUSE", "0"))) # a slow agent: time to kill the emulator mid-run
|
|
15
|
+
for method, path, body in (("POST", "/v1/customers", b"email=rosa%40example.com"), ("GET", "/v1/customers", None)):
|
|
16
|
+
asked = urllib.request.Request(
|
|
17
|
+
f"https://api.stripe.com{path}", data=body, method=method, headers={"authorization": "Bearer sk_test_123"}
|
|
18
|
+
)
|
|
19
|
+
try:
|
|
20
|
+
urllib.request.urlopen(asked, timeout=30).read()
|
|
21
|
+
except urllib.error.HTTPError as refused:
|
|
22
|
+
print(f"{method} {path}: {refused.code} {refused.read()[:200]!r}", file=sys.stderr)
|
|
23
|
+
done = wake["reason"] != "start" and now.hour >= 12
|
|
24
|
+
print(json.dumps({"status": "done" if done else "idle", "next_wake": (now + timedelta(hours=1)).isoformat()}))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# The agent file: the payments API's real host is forwarded to stripe-mock, a ready-made open-source emulator,
|
|
2
|
+
# run in a container Minutehand starts on a free port and stops with the run.
|
|
3
|
+
name: payments_example
|
|
4
|
+
wakes:
|
|
5
|
+
- {kind: command, argv: [python, agent.py]}
|
|
6
|
+
outbound:
|
|
7
|
+
- host: api.stripe.com
|
|
8
|
+
kind: forward
|
|
9
|
+
emulator: stripe
|
|
10
|
+
emulators:
|
|
11
|
+
- name: stripe
|
|
12
|
+
upstream: {url: "http://127.0.0.1:{port}"}
|
|
13
|
+
command: [docker, run, --rm, --name, minutehand-ext-stripe, -p, "{port}:12111", "stripe/stripe-mock:latest"]
|
|
14
|
+
ready: {kind: http, path: /v1/charges, status: 401} # answers 401 to a call with no key once it is up
|
|
15
|
+
ready_within: PT60S
|
|
16
|
+
health: {every: PT1S, fails: 2}
|
|
17
|
+
faithful: [{status: 402}] # a declined card is Stripe's own answer
|