interlatch 0.26.0__tar.gz → 0.27.0__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.
- {interlatch-0.26.0 → interlatch-0.27.0}/CHANGELOG.md +26 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/PKG-INFO +1 -1
- interlatch-0.27.0/changelog.d/party-formations.md +4 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/party.md +16 -6
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/privacy.md +1 -1
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/party.md +17 -6
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/privacy.md +1 -1
- {interlatch-0.26.0 → interlatch-0.27.0}/leaderboard/firestore.rules +13 -5
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/i18n/ja.py +5 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/leaderboard.py +13 -6
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/party.py +57 -1
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/handler.py +3 -3
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/routes.py +7 -5
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/arena.py +74 -47
- interlatch-0.27.0/src/interlatch/knowledge/formation.py +165 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/arena.py +59 -26
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/leaderboard.py +19 -15
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/innerlatch.css +79 -2
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/innerlatch.js +202 -63
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/ja.js +44 -6
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/scene.js +17 -11
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_arena.py +119 -31
- interlatch-0.27.0/tests/test_formation.py +154 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_route_matrix.py +4 -2
- interlatch-0.26.0/changelog.d/adventure.md +0 -5
- interlatch-0.26.0/changelog.d/group-page.md +0 -7
- interlatch-0.26.0/changelog.d/hub-join-http-client.md +0 -2
- interlatch-0.26.0/changelog.d/install-choose-analyzer.md +0 -4
- interlatch-0.26.0/changelog.d/town-and-gear.md +0 -5
- {interlatch-0.26.0 → interlatch-0.27.0}/.github/dependabot.yml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/.github/workflows/changelog.yml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/.github/workflows/ci.yml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/.github/workflows/docs.yml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/.github/workflows/release.yml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/.gitignore +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/.pre-commit-config.yaml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/.python-version +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/CONTRIBUTING.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/LICENSE +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/README.ja.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/ROADMAP.ja.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/ROADMAP.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/SECURITY.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/careers/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/careers/boards.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/careers/pipeline.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/changelog.d/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/dev.sh +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docker/.dockerignore +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docker/.env.example +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docker/Caddyfile +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docker/Dockerfile +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docker/compose.yaml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docker/wheels/.gitignore +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/_site/hooks.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/analysis.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/cli.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/configuration.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/dashboard.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/demo/make_demo.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/demo/record_demo.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/design/ja/learning.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/design/learning.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/development.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/devices.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/diagrams/analysis.excalidraw.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/diagrams/architecture.excalidraw.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/docker.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/faq.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/images/demo.gif +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/images/home.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/images/map.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/images/palette.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/images/session.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/install.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/analysis.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/cli.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/configuration.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/dashboard.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/development.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/devices.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/docker.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/faq.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/install.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/join-a-hub.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/mcp.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/moving-from-chronicle.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/postgres.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/sources.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/suggestions.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/troubleshooting.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/ja/vscode.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/join-a-hub.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/mcp.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/moving-from-chronicle.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/postgres.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/sources.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/suggestions.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/troubleshooting.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/docs/vscode.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/leaderboard/.firebaserc +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/leaderboard/.gitignore +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/leaderboard/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/leaderboard/firebase.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/leaderboard/firestore.indexes.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/mkdocs.yml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/art.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/filing.html +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/loops.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/loops.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/members.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/render.html +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/render.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/scenes-more.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/icons3d/scenes.html +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/Chronicle.icns +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/Interlatch.spec +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/build.sh +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/entitlements.plist +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/icon-3d.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/icon-blueprint-prompt.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/icon-blueprint.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/icon.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/interlatch_app.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/macos/make_icon.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/packaging/release.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/pyproject.toml +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/chronicle/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/chronicle/__main__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/chronicle/windows.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/__main__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/digest.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/language.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/queue.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/api.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/base.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/bob.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/claude.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/codex.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/factory.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/process.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/replies.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/runners/settings.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/screen.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/session.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/analysis/worker.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/application/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/application/records.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/application/search.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/application/workflows.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/artifacts/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/artifacts/files.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/artifacts/queries.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/cli.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/agents.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/artifact_records.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/config.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/diagrams.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/i18n/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/machine.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/paths.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/pricing.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/process.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/redact.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/sessions.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/core/util.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/analyses.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/archive.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/chat_import.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/deletion.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/history.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/memory.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/persistence.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/report.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/ingestion/service.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/commands/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/commands/archive.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/commands/knowledge.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/commands/operations.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/commands/recording.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/commands/setup.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/commands/sharing.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/common.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/main.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/cli/parser.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/exports/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/exports/diagrams.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/exports/markdown.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/exports/sessions.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/exports/vault.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/mcp.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/artifacts.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/common.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/devices.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/hub_admin.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/knowledge.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/projects.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/sessions.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/settings.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/sources.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/status.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/storage.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/api/team.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/app.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/assets.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/context.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/jobs.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/interfaces/web/server.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/bag.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/cases.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/friction.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/glossary.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/instructions.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/ladder.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/learning.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/market.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/party.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/reviews.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/roadmap.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/suggest.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/knowledge/synthesize.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/projects/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/projects/groups.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/projects/labels.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/projects/progress.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/projects/routing.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/projects/systems.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/connectors.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/container.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/hooks.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/install.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/migrate.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/notify.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/platforms/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/platforms/desktop.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/platforms/menubar.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/platforms/windows.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/statusline.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/tailnet.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/runtime/update.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/access.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/client.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/identity.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/market.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/news.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/people.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/projects.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/protocol.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/pull.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/push.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sharing/receiver.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/antigravity.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/bob.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/builder.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/chatgpt.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/chats.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/claude/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/claude/export.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/claude/files.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/claude/parser.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/codex/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/codex/cloud.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/codex/parser.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/copilot.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/discovery.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/opencode.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/sources/prompts.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/__init__.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/fts.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/kv.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/migrations.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/mirror.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/postgres.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/schema.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/sqlite.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/storage/team.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/AGENT-ICONS.txt +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/PLEX-OFL.txt +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/adventure.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-antigravity.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-bob.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-chatgpt.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-claude-ai.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-claude.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-codex.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-copilot.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/agent-opencode.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/app.css +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/app.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-book.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-camera.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-chip.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-clock.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-cloud.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-coins.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-container.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-dashboard.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-dex.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-document.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-flag.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-folder.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-gateway.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-gears.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-glitch.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-globe.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-laptop.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-log.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-lumi.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-magnifier.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-pip.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-plug.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-rack.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/art-tink.webp +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/battle.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/boot.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/favicon.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/i18n.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/icon-180.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/icon-192.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/icon-512.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/icon.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/index.html +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/learning.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/manifest.webmanifest +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/panel.css +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/panel.html +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/panel.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/plex-mono-400.woff2 +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/plex-mono-500.woff2 +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/plex-mono-600.woff2 +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/plex-sans-400.woff2 +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/plex-sans-500.woff2 +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/plex-sans-600.woff2 +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/plex-sans-700.woff2 +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/rough.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/web/sprites.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/src/interlatch/windows.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/antigravity_fixture.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/codex_fixture.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/conftest.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/copilot_fixture.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/friction_fixture.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/golden.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/cli.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/diagram.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/parsers/antigravity.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/parsers/bob.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/parsers/claude_code.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/parsers/codex.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/parsers/copilot.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/parsers/opencode.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/goldens/systems.json +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/opencode_fixture.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_adventure_web.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_analysis.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_antigravity.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_architecture.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_artifacts.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_bag.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_battle_web.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_bob_analysis.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_boot_web.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_careers.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_case_files.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_cases.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_chat_import.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_cli_pins.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_codex.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_codex_analysis.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_codex_cloud.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_container.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_copilot_bob.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_dedicated_hub.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_dedupe.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_desktop.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_diagram.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_ee_boundary.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_friction.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_glossary.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_group_sharing.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_home_agents.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_hub.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_hub_accept.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_hub_news.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_hub_projects.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_hub_projects_listed.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_hub_purge.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_hub_sharing_race.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_i18n_web.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_ingest.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_instructions.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_integration.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_ladder.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_language.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_leaderboard.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_learning_glance.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_learning_interests.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_learning_web.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_leave_project.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_legacy_package.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_map_goldens.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_market.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_menubar.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_migrate.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_mirror.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_notify.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_opencode.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_parser.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_parser_goldens.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_party.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_party_map.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_people.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_people_hub.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_people_web.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_project_groups.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_project_progress.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_providers.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_queue.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_regressions.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_release.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_roadmap_catalog.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_screen.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_security.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_setup.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_statusline.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_suggest.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_suggest_cli.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_systems.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_team.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_team_home.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_update.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/test_windows.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/tests/windows_smoke.py +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/uv.lock +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/vscode-extension/.vscodeignore +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/vscode-extension/LICENSE +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/vscode-extension/README.md +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/vscode-extension/extension.js +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/vscode-extension/media/icon.png +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/vscode-extension/media/interlatch.svg +0 -0
- {interlatch-0.26.0 → interlatch-0.27.0}/vscode-extension/package.json +0 -0
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
<!-- Don't add lines here: a pull request puts its line in a new file in changelog.d/ (see changelog.d/README.md),
|
|
4
4
|
and each release moves the files it took into a dated section below. -->
|
|
5
5
|
|
|
6
|
+
## 0.26.0 (2026-10-16)
|
|
7
|
+
|
|
8
|
+
- **Adventure:** a new page in Innerlatch puts every lesson that counts on a pixel world map, one region per attribute
|
|
9
|
+
and one territory per roadmap module, fogged where no lesson reached yet. Click a monster for a turn-based battle
|
|
10
|
+
(Attack, Skill, Defend, Run) led by the agent that taught it; a win marks it cleared, and battles never change
|
|
11
|
+
levels. The Party page keeps the characters and quests, and a lesson link anywhere in Innerlatch now opens the lesson
|
|
12
|
+
in a window over the page instead of leaving for the dashboard ([Party](docs/party.md#adventure)).
|
|
13
|
+
- **Choose what analyzes sessions at install:** `interlatch install` now lists every choice (Claude Code, Codex,
|
|
14
|
+
IBM Bob and the model providers' APIs, Ollama included) with whether each is ready, and sets up the one you pick:
|
|
15
|
+
its endpoint, API key and model, then one test call. `--backend <name>` chooses without asking
|
|
16
|
+
([Install](docs/install.md#command-line)).
|
|
17
|
+
- **Joining a hub works again:** `interlatch hub join` no longer stops with "module 'http' has no attribute 'client'"
|
|
18
|
+
on a fresh install.
|
|
19
|
+
- **A group's page:** **Analyze** on a project group's heading on the Projects page (or **The whole group** in the
|
|
20
|
+
sidebar) reads the group's projects as one big project. It shows each project's share of the group's time, cost and
|
|
21
|
+
knowledge, what waits on you across all of them, where their last sessions left off, each folder in git, their
|
|
22
|
+
roadmaps and their work week by week, with every item named by its project. A **Map** draws the projects and the
|
|
23
|
+
services and platforms they use, hand-drawn, with the ones they share easy to spot and an Excalidraw download; Usage
|
|
24
|
+
charts each project's time week by week. Each project's knowledge base and architecture sketch sit beside the
|
|
25
|
+
others, and the glossary terms the projects share come first ([Project groups](docs/dashboard.md#project-groups)).
|
|
26
|
+
- **Towns and gear:** each Adventure region has a town whose shop sells gear for its roadmap modules (a weapon, armor
|
|
27
|
+
and charm per module, in three tiers that open as your agents reach Lv 1, 3 and 5 in that skill) and battle supplies.
|
|
28
|
+
A lesson's first win pays gold and may drop loot of its kind; battles gain an **Item** command, and each agent
|
|
29
|
+
wears three slots, changed on its page or in the new **Bag**. Gear only changes battle numbers, never levels
|
|
30
|
+
([Party](docs/party.md#towns-and-gear)).
|
|
31
|
+
|
|
6
32
|
## 0.25.0 (2026-10-15)
|
|
7
33
|
|
|
8
34
|
- **Innerlatch:** the Party, the Leaderboard, the Arena and their Google sign-in moved out of the dashboard into
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: interlatch
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.27.0
|
|
4
4
|
Summary: Shared memory for your coding agents: records every session (Claude Code, Codex, Copilot, Bob, Antigravity, OpenCode) locally and keeps what they learned, each lesson linked to the session it came from.
|
|
5
5
|
Project-URL: Homepage, https://interlatch.com
|
|
6
6
|
Project-URL: Documentation, https://interlatch.com/docs/
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
- **Formations:** the Party page builds decks, teams of one to three agents in the order they fight, up to five
|
|
2
|
+
of them. The Arena fields the deck you pick, as a relay: lead against lead, and whoever wins a bout stays on with
|
|
3
|
+
the HP it has left. A deck of one is the old champion duel. Sheets now carry the lineup (format 3), so arena players
|
|
4
|
+
on older versions don't meet newer ones until they update ([Party](docs/party.md#formations)).
|
|
@@ -85,18 +85,28 @@ Lv 1 に達すると I 段階、Lv 3 で II 段階、Lv 5 で III 段階が店
|
|
|
85
85
|
身につけます。エージェントのページか**バッグ**で付け替え、バッグでは要らないものを売れます。装備で変わるのは戦闘の数値だけで、
|
|
86
86
|
レベルや経験値は教訓からだけ得られます。
|
|
87
87
|
|
|
88
|
+
## 編成
|
|
89
|
+
|
|
90
|
+
パーティー画面の**編成**で**デッキ**を作れます。デッキは 1〜3 人のエージェントを戦う順番(先鋒・中堅・大将)に並べたチームで、
|
|
91
|
+
名前をつけて 5 つまで持てます。枠の下の**控え**からエージェントを入れ、矢印かドラッグで順番を入れ替えます。控えのエージェントを
|
|
92
|
+
枠にドラッグすると入れ替わります。何も変えないうちは、レベルの高い 3 人のデッキが 1 つあります(デッキができる前にチャンピオンを
|
|
93
|
+
選んでいた場合は、そのエージェントとレベルの高い 2 人)。アリーナに出すデッキは、ここかアリーナのページの
|
|
94
|
+
**アリーナに出場させる**で選びます。デッキで変わるのは誰がどの順番で戦うかだけで、レベルは変わりません。
|
|
95
|
+
|
|
88
96
|
## アリーナ
|
|
89
97
|
|
|
90
98
|
リーダーボードに参加している人は、パーティーを**アリーナ**に出すこともできます。アリーナは Innerlatch の独立したページです。
|
|
91
99
|
Innerlatch のレールで**アリーナ**を開き、**アリーナに入る**を選びます(先にボードに参加します)。それからは同期のたびに、パーティーのキャラクター
|
|
92
|
-
シートを公開します。コーディングエージェントそれぞれのレベル、ランク、クラス、6
|
|
93
|
-
|
|
100
|
+
シートを公開します。コーディングエージェントそれぞれのレベル、ランク、クラス、6 つの能力値のレベルと、出場させるチーム
|
|
101
|
+
(どのエージェントをどの順番で出すか)、ボードに表示する名前と画像、コンピューターを見分けるコード(名前ではなくハッシュ)です。教訓、プロジェクト、
|
|
94
102
|
セッション、タイトル、プロンプトは送りません。
|
|
95
103
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
アリーナはチーム同士の勝ち抜き戦です。あなたのチームは、出場させているデッキです([編成](#編成))。毎週、ほかのすべての
|
|
105
|
+
プレイヤーのチームと 1 回ずつ戦います。先鋒どうしがラウンドごとに 1 対 1 で戦い、勝ったほうは残りの HP のまま相手の次の
|
|
106
|
+
エージェントと戦います。40 ラウンドたっても決着しないときは、HP の残りが少ないほうが敗退します。先にエージェントがいなくなった
|
|
107
|
+
チームの負けです。1 人のデッキ同士なら、ふつうの一騎打ちです。アリーナのページは勝利数と、各プレイヤーのチームを並べます。
|
|
108
|
+
ライバルをクリックすると対戦を見られます。左があなたのチーム、右が相手で、同じ戦いが下に記録として書かれ、その下に両方の
|
|
109
|
+
パーティーが並びます。対戦は公開された 2 つのシートとその週から、あなた自身の
|
|
100
110
|
コンピューターで計算されるので、2 人とも同じ結果を見ます。数字は自己申告で、記録ではなくゲームです。**アリーナを出る**と
|
|
101
111
|
シートは削除され、ボードには残ります。ボードから抜けるとアリーナからも出ます。
|
|
102
112
|
|
|
@@ -18,7 +18,7 @@ Interlatch の Firebase プロジェクトから、公開されている求人
|
|
|
18
18
|
このリクエストに含まれるのはプロジェクトの公開ウェブキーだけで、サインインを含め、あなたに関する情報は何も送りません。
|
|
19
19
|
さらにアリーナのページで**アリーナに入る**と、同期のたびにパーティーのキャラクターシートを同じ Firebase プロジェクトに
|
|
20
20
|
公開し、誰でも読めるようになります。コーディングエージェントそれぞれのレベル、ランク、クラス、6 つの能力値のレベルと、
|
|
21
|
-
|
|
21
|
+
出場させるチーム(どのエージェントをどの順番で出すか)、ボードの名前と画像、コンピューターを見分けるハッシュです。教訓、プロジェクト、セッション、タイトル、プロンプトは送りません。
|
|
22
22
|
対戦は公開されたシートからあなたのコンピューターで計算されます。**アリーナを出る**(またはボードから抜ける)とシートは削除されます
|
|
23
23
|
([アリーナ](party.md#アリーナ))。
|
|
24
24
|
claude.ai や ChatGPT のエクスポートを取り込むときはチャットだけを読み、アカウントのファイル(`users.json`、`user.json`)は開きません。
|
|
@@ -95,18 +95,29 @@ ADR Scroll from a decision. Use supplies and loot from the **Item** command in a
|
|
|
95
95
|
(weapon, armor, charm): change them on the agent's page or in the **Bag**, where you can also sell what you don't need.
|
|
96
96
|
Gear only changes battle numbers: levels and XP still come from lessons alone.
|
|
97
97
|
|
|
98
|
+
## Formations
|
|
99
|
+
|
|
100
|
+
On the Party page, **Formation** builds **decks**: teams of one to three of your agents, in the order they fight
|
|
101
|
+
(lead, middle, anchor). Keep up to five, each with a name of your choice. Add an agent from the **Bench** below the
|
|
102
|
+
slots, reorder with the arrows or by dragging, and drag a bench agent onto a slot to swap it in. Until you change
|
|
103
|
+
anything, your party has one deck: your three highest-level agents (or, if you picked a champion before decks
|
|
104
|
+
existed, that agent and then the two highest). One deck is the one the Arena fields: pick it with **Field in the
|
|
105
|
+
Arena**, here or on the Arena page. A deck changes who fights and in what order, never a level.
|
|
106
|
+
|
|
98
107
|
## The arena
|
|
99
108
|
|
|
100
109
|
People on the leaderboard can also put their parties in the **Arena**, a page of its own in Innerlatch. Open
|
|
101
110
|
**Arena** in Innerlatch's rail and choose **Enter the arena** (you join the board first). From then on each sync publishes your party's
|
|
102
|
-
character sheet: for each of your coding agents its level, tier, class and six attribute levels, and
|
|
103
|
-
|
|
111
|
+
character sheet: for each of your coding agents its level, tier, class and six attribute levels, and the team you
|
|
112
|
+
field (which agents, in what order), with the name and picture you show on the board and the code that tells your computers apart (a
|
|
104
113
|
hash, not a name). No lessons, projects, sessions, titles or prompts go.
|
|
105
114
|
|
|
106
|
-
The arena is
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
the
|
|
115
|
+
The arena is team against team, in a relay. Your team is the deck you field ([Formations](#formations)). Every
|
|
116
|
+
week it fights every other player's team once: the two leads fight one on one, round by round; whoever wins the bout
|
|
117
|
+
stays on, with the HP it has left, against the other side's next agent; a bout still going after 40 rounds puts out
|
|
118
|
+
the one with less of its HP left. The team that runs out of agents loses. A deck of one against a deck of one is a
|
|
119
|
+
plain duel. The Arena page ranks you by wins, with each player's team. Click a rival to watch the battle: your team on
|
|
120
|
+
the left, theirs on the right, with the same fight written out as a log underneath and both full parties below. Each battle is worked out on your own
|
|
110
121
|
computer from the two published sheets and the week, so both players see the same result. The numbers are
|
|
111
122
|
self-reported: it's a game, not a record. **Leave the arena** deletes your sheet; you stay on the board. Leaving the
|
|
112
123
|
board leaves the arena too.
|
|
@@ -20,7 +20,7 @@ public job-market numbers (open roles per roadmap track) from Interlatch's Fireb
|
|
|
20
20
|
That request carries the project's public web key and nothing about you, not even your sign-in.
|
|
21
21
|
If you also **Enter the arena** on the Arena page, each sync publishes your party's character sheet to the same
|
|
22
22
|
Firebase project, readable by anyone: for each of your coding agents its level, tier, class and six attribute
|
|
23
|
-
levels, and which
|
|
23
|
+
levels, and the team you field (which agents, in what order), with your board name and picture and a hash that tells your computers apart. No lessons, projects, sessions,
|
|
24
24
|
titles or prompts. Battles are worked out on your computer from the published sheets; **Leave the arena** (or leaving
|
|
25
25
|
the board) deletes your sheet ([The arena](party.md#the-arena)).
|
|
26
26
|
Importing a claude.ai or ChatGPT export reads only the chats, never the account files (`users.json`, `user.json`).
|
|
@@ -111,12 +111,14 @@ service cloud.firestore {
|
|
|
111
111
|
&& data.at == request.time;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// The arena: a party's sheet (src/interlatch/sharing/arena.py writes it). Fields (all required, no others):
|
|
115
|
-
// name, photo, machine: as for players; v: int, the sheet format (2);
|
|
114
|
+
// The arena: a party's sheet (src/interlatch/sharing/arena.py writes it). Fields (all required but lineup, no others):
|
|
115
|
+
// name, photo, machine: as for players; v: int, the sheet format (2, or 3 with a lineup);
|
|
116
116
|
// agents: a map of the coding agents, each {level: int 1-200, tier: novice|adept|expert|master,
|
|
117
117
|
// cls: null or a track id (<= 64), attrs: {craft, data, ops, guard, arcana, lore: int}}: always the first
|
|
118
118
|
// five, and opencode from Interlatch versions that know it (sheets from older ones have no opencode);
|
|
119
|
-
// champion: which of them fights for the party;
|
|
119
|
+
// champion: which of them fights first for the party;
|
|
120
|
+
// lineup (v 3 only, and required there): the deck it fields, 1-3 of its agents in order, each once, the champion
|
|
121
|
+
// first (v 2 sheets, from versions before decks, field the champion alone);
|
|
120
122
|
// at: timestamp, the server time of the upload (request.time). No lessons, projects or titles.
|
|
121
123
|
// Cheap on purpose: a request may evaluate at most 1000 expressions, and the full bounds on every attribute (as
|
|
122
124
|
// before) went over it with five agents, so no sheet could be written. The values' bounds are left to the readers,
|
|
@@ -132,17 +134,23 @@ service cloud.firestore {
|
|
|
132
134
|
&& a.attrs.guard is int && a.attrs.arcana is int && a.attrs.lore is int;
|
|
133
135
|
}
|
|
134
136
|
|
|
137
|
+
function isValidArenaLineup(data) {
|
|
138
|
+
return data.lineup is list && data.lineup.size() >= 1 && data.lineup.size() <= 3
|
|
139
|
+
&& data.lineup.toSet().size() == data.lineup.size() && data.lineup.hasOnly(data.agents.keys())
|
|
140
|
+
&& data.lineup[0] == data.champion;
|
|
141
|
+
}
|
|
142
|
+
|
|
135
143
|
function isValidArena(data) {
|
|
136
144
|
let fields = ['name', 'photo', 'machine', 'v', 'agents', 'champion', 'at'];
|
|
137
145
|
let agents = ['claude', 'codex', 'copilot', 'bob', 'antigravity', 'opencode'];
|
|
138
146
|
let required = ['claude', 'codex', 'copilot', 'bob', 'antigravity'];
|
|
139
|
-
return hasOnlyAllowedFields(fields) && hasRequiredFields(fields)
|
|
147
|
+
return hasOnlyAllowedFields(fields.concat(['lineup'])) && hasRequiredFields(fields)
|
|
140
148
|
&& data.name is string && data.name.size() >= 1 && data.name.size() <= 64
|
|
141
149
|
&& data.photo is string && data.photo.size() <= 500
|
|
142
150
|
&& (data.photo == ''
|
|
143
151
|
|| data.photo.matches('^https://(lh3[.]googleusercontent[.]com|avatars[.]githubusercontent[.]com)/[^\\s]+$'))
|
|
144
152
|
&& data.machine is string && data.machine.matches('^[A-Za-z0-9-]{8,64}$')
|
|
145
|
-
&& data.v == 2
|
|
153
|
+
&& ((data.v == 2 && !('lineup' in data)) || (data.v == 3 && isValidArenaLineup(data)))
|
|
146
154
|
&& data.champion in agents
|
|
147
155
|
&& data.agents is map && data.agents.keys().hasOnly(agents) && data.agents.keys().hasAll(required)
|
|
148
156
|
&& isValidArenaAgent(data.agents.claude) && isValidArenaAgent(data.agents.codex)
|
|
@@ -373,6 +373,11 @@ JA: dict[str, str] = {
|
|
|
373
373
|
"that doesn't go in this slot": "それはこの装備欄には付けられません",
|
|
374
374
|
"only consumables and loot can be used": "使えるのは消耗品と戦利品だけです",
|
|
375
375
|
"none left": "もう残っていません",
|
|
376
|
+
# ---- formations (formation.py)
|
|
377
|
+
"no such deck": "そのデッキはありません",
|
|
378
|
+
"a party holds at most 5 decks": "デッキは 5 つまでです",
|
|
379
|
+
"a party keeps at least one deck": "デッキは最低 1 つ必要です",
|
|
380
|
+
"a deck holds one to three of your agents, each once": "デッキには 1〜3 人のエージェントを、重複なしで入れてください",
|
|
376
381
|
"{tier} {noun} of {module}": "{tier}の{noun}({module})",
|
|
377
382
|
"+{atk} attack, and +{skill} to the {module} skill, in battle only": "攻撃 +{atk}、{module} のスキル +{skill}(戦闘中のみ)",
|
|
378
383
|
"+{hp} HP, and {cut}% less damage taken, in battle only": "HP +{hp}、受けるダメージ {cut}% 減(戦闘中のみ)",
|
|
@@ -23,8 +23,8 @@ def leaderboard(ctx: Context, q: dict) -> dict:
|
|
|
23
23
|
try:
|
|
24
24
|
out["rows"] = arena.board(me.get("uid") if me.get("arena") else None)
|
|
25
25
|
out["week"] = arena.week()
|
|
26
|
-
if me.get("signed_in"): #
|
|
27
|
-
out["me"] = {**me,
|
|
26
|
+
if me.get("signed_in"): # the team this computer fields, and the decks it can pick from
|
|
27
|
+
out["me"] = {**me, **_fielding(ctx)}
|
|
28
28
|
except lb.LeaderboardError as e:
|
|
29
29
|
out["error"] = str(e)
|
|
30
30
|
return out
|
|
@@ -101,13 +101,20 @@ def action_arena(ctx: Context, which: str) -> dict:
|
|
|
101
101
|
return lb.status(ctx.cfg)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
def
|
|
105
|
-
"""Field another champion: one of the five coding agents; sent now when in the arena."""
|
|
104
|
+
def _fielding(ctx: Context) -> dict:
|
|
106
105
|
from ....sharing import arena
|
|
107
106
|
from ....sharing import leaderboard as lb
|
|
108
107
|
|
|
109
|
-
arena.
|
|
110
|
-
|
|
108
|
+
return arena.fielding(ctx.cfg, ctx.conn, lb.read_state(ctx.cfg))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def action_arena_deck(ctx: Context, body: dict) -> dict:
|
|
112
|
+
"""Field another of this computer's decks; sent now when in the arena."""
|
|
113
|
+
from ....sharing import arena
|
|
114
|
+
from ....sharing import leaderboard as lb
|
|
115
|
+
|
|
116
|
+
arena.pick(ctx.cfg, ctx.conn, body.get("deck"))
|
|
117
|
+
return {**lb.status(ctx.cfg), **_fielding(ctx)}
|
|
111
118
|
|
|
112
119
|
|
|
113
120
|
def action_leaderboard_join(ctx: Context) -> dict:
|
|
@@ -47,7 +47,25 @@ def party(ctx: Context, viewer: dict | None, *, here: bool) -> dict:
|
|
|
47
47
|
return {"signed_in": False, **gate, **({"preview": pt.preview(ctx.conn, _here_id(ctx))} if here else {})}
|
|
48
48
|
data = pt.compute(ctx.conn, _here_id(ctx), viewer)
|
|
49
49
|
# who's hiring for the classes it shows: read from Firebase only now, signed in (the numbers are public)
|
|
50
|
-
return {"signed_in": True, **gate, "who": me, **data, "market": pt.market_block(pt.payload_tracks(data))
|
|
50
|
+
return {"signed_in": True, **gate, "who": me, **data, "market": pt.market_block(pt.payload_tracks(data)),
|
|
51
|
+
"formations": _formations(ctx, viewer, data)}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _levels(data: dict) -> dict[str, int]:
|
|
55
|
+
return {a["agent"]: a["level"] for a in data.get("agents") or []}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _lead(ctx: Context, viewer: dict | None):
|
|
59
|
+
"""The champion picked on this computer before decks existed: the first of the deck made for its party."""
|
|
60
|
+
from ....sharing import leaderboard as lb
|
|
61
|
+
|
|
62
|
+
return (lb.read_state(ctx.cfg) or {}).get("champion") if viewer is None else None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _formations(ctx: Context, viewer: dict | None, data: dict) -> dict:
|
|
66
|
+
from ....knowledge import formation
|
|
67
|
+
|
|
68
|
+
return formation.formations(ctx.conn, viewer, _levels(data), _lead(ctx, viewer))
|
|
51
69
|
|
|
52
70
|
|
|
53
71
|
def _refused() -> tuple[dict, int]:
|
|
@@ -160,6 +178,44 @@ def action_party_bag(ctx: Context, viewer: dict | None, what: str, body: dict, *
|
|
|
160
178
|
return {"bag": got}, 200
|
|
161
179
|
|
|
162
180
|
|
|
181
|
+
def action_party_deck(ctx: Context, viewer: dict | None, body: dict, *, here: bool) -> tuple[dict, int]:
|
|
182
|
+
"""POST /api/party/deck: add a deck (no "id"), rename it or change its members in order ("name", "members"),
|
|
183
|
+
delete it ("delete": true), or field it in the Arena ("arena": true, this computer's own party only). Answers the
|
|
184
|
+
decks; when the team this computer fields changed and it is in the arena, the sheet is sent now."""
|
|
185
|
+
from ....knowledge import formation
|
|
186
|
+
from ....knowledge import party as pt
|
|
187
|
+
|
|
188
|
+
if who(ctx, viewer) is None:
|
|
189
|
+
return _refused()
|
|
190
|
+
deck = body.get("id")
|
|
191
|
+
levels = _levels(pt.compute(ctx.conn, _here_id(ctx), viewer))
|
|
192
|
+
lead = _lead(ctx, viewer)
|
|
193
|
+
before = formation.formations(ctx.conn, viewer, levels, lead)
|
|
194
|
+
try:
|
|
195
|
+
if deck is not None and not isinstance(deck, str):
|
|
196
|
+
raise pt.PartyError(formation.NO_DECK)
|
|
197
|
+
if body.get("delete"):
|
|
198
|
+
got = formation.delete(ctx.conn, viewer, levels, deck, lead=lead)
|
|
199
|
+
elif body.get("arena"):
|
|
200
|
+
if viewer is not None: # a hub person's party never enters the arena
|
|
201
|
+
raise pt.PartyError(formation.NO_DECK)
|
|
202
|
+
got = formation.field(ctx.conn, viewer, levels, deck, lead=lead)
|
|
203
|
+
else:
|
|
204
|
+
got = formation.save(ctx.conn, viewer, levels, deck, name=body.get("name"), members=body.get("members"),
|
|
205
|
+
lead=lead)
|
|
206
|
+
except pt.PartyError as exc:
|
|
207
|
+
return {"error": tr(str(exc))}, 400
|
|
208
|
+
if viewer is None and _fielded(before) != _fielded(got):
|
|
209
|
+
from ....sharing import arena
|
|
210
|
+
|
|
211
|
+
arena.resend(ctx.cfg, ctx.conn)
|
|
212
|
+
return {"formations": got}, 200
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _fielded(f: dict) -> list[str]:
|
|
216
|
+
return next((d["members"] for d in f["decks"] if d["id"] == f["arena"]), [])
|
|
217
|
+
|
|
218
|
+
|
|
163
219
|
def action_party_quest(ctx: Context, viewer: dict | None, body: dict, *, here: bool) -> tuple[dict, int]:
|
|
164
220
|
"""Mark a quest done or not, in the viewer's own log; answers the party as it is now."""
|
|
165
221
|
from ....knowledge import party as pt
|
|
@@ -28,10 +28,10 @@ LEGACY_SESSION_COOKIE = "chronicle_session" # the same, signed in before the re
|
|
|
28
28
|
PROXY_HEADERS = ("X-Forwarded-For", "Tailscale-User-Login", "X-Forwarded-Proto", "X-Real-IP", "Forwarded")
|
|
29
29
|
HUB_PUSH = ("/api/hub/file", "/api/hub/sessions", "/api/hub/analyses", "/api/hub/done", "/api/hub/hello",
|
|
30
30
|
"/api/hub/withdraw")
|
|
31
|
-
# POSTs any person signed in to a hub may make, not only admins: their own Party quest log, goal, cleared encounters
|
|
32
|
-
# and
|
|
31
|
+
# POSTs any person signed in to a hub may make, not only admins: their own Party quest log, goal, cleared encounters,
|
|
32
|
+
# bag and decks (routes.PARTY decides)
|
|
33
33
|
PARTY_OWN = ("/api/party/quest", "/api/party/goal", "/api/party/cleared", "/api/party/buy", "/api/party/sell",
|
|
34
|
-
"/api/party/equip", "/api/party/use")
|
|
34
|
+
"/api/party/equip", "/api/party/use", "/api/party/deck")
|
|
35
35
|
|
|
36
36
|
log = logging.getLogger("interlatch.server")
|
|
37
37
|
SIGNIN_HELP = ("Ask an admin of this hub for a new invite, or open the hub's dashboard again from your own Interlatch "
|
|
@@ -293,8 +293,8 @@ def _arena(h, ctx, m, body): # what this computer publishes about its party: on
|
|
|
293
293
|
from ...sharing.leaderboard import LeaderboardError
|
|
294
294
|
|
|
295
295
|
try:
|
|
296
|
-
if m.group(1) == "
|
|
297
|
-
return h._json(api.leaderboard.
|
|
296
|
+
if m.group(1) == "deck":
|
|
297
|
+
return h._json(api.leaderboard.action_arena_deck(ctx, _body(body)))
|
|
298
298
|
return h._json(api.leaderboard.action_arena(ctx, m.group(1)))
|
|
299
299
|
except LeaderboardError as e:
|
|
300
300
|
return h._json({"error": str(e)}, 502)
|
|
@@ -311,7 +311,9 @@ def _party_sign(h, ctx, m, body): # the Party's Google sign-in: the leaderboard
|
|
|
311
311
|
return h._json({"error": str(e)}, 502)
|
|
312
312
|
|
|
313
313
|
|
|
314
|
-
def _party_own(h, ctx, m, body): # the viewer's own quest log, goal, cleared encounters and
|
|
314
|
+
def _party_own(h, ctx, m, body): # the viewer's own quest log, goal, cleared encounters, bag and decks
|
|
315
|
+
if m.group(1) == "deck":
|
|
316
|
+
return h._json(*api.party.action_party_deck(ctx, h.viewer, _body(body), here=h._from_here()))
|
|
315
317
|
if m.group(1) in ("buy", "sell", "equip", "use"):
|
|
316
318
|
return h._json(*api.party.action_party_bag(ctx, h.viewer, m.group(1), _body(body), here=h._from_here()))
|
|
317
319
|
act = {"quest": api.party.action_party_quest, "goal": api.party.action_party_goal,
|
|
@@ -369,11 +371,11 @@ def _suggestions_refresh(h, ctx, m, body):
|
|
|
369
371
|
POST: list[Route] = [
|
|
370
372
|
Route("/api/sync", ANYONE, lambda h, ctx, m, b: h._json({"started": api.status.action_sync(ctx)})),
|
|
371
373
|
Route(r"/api/leaderboard/(signin|finish|cancel|join|send|name|leave)", LEADERBOARD, _leaderboard),
|
|
372
|
-
Route(r"/api/leaderboard/arena/(join|leave|
|
|
374
|
+
Route(r"/api/leaderboard/arena/(join|leave|deck)", LEADERBOARD, _arena),
|
|
373
375
|
# a Google sign-in kept on this computer: only from here
|
|
374
376
|
Route(r"/api/party/(signin|signout)", HERE, _party_sign),
|
|
375
377
|
# the viewer's own quest log: from here, or any person signed in to a hub, not only admins (handler.PARTY_OWN)
|
|
376
|
-
Route(r"/api/party/(quest|goal|cleared|buy|sell|equip|use)", PARTY, _party_own),
|
|
378
|
+
Route(r"/api/party/(quest|goal|cleared|buy|sell|equip|use|deck)", PARTY, _party_own),
|
|
377
379
|
Route(r"/api/artifacts/(\d+)/reveal", LOCAL_FILES, _reveal),
|
|
378
380
|
# a password: at the hub itself, or an admin of a hub with people
|
|
379
381
|
Route("/api/team-store/test", OWNER, lambda h, ctx, m, b: h._json(api.hub_admin.action_team_store_test(ctx, b))),
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
"""The Arena:
|
|
2
|
-
|
|
3
|
-
(sharing/arena.py publishes and reads the sheets). Pure functions:
|
|
1
|
+
"""The Arena: team against team. Two published party sheets each field a lineup, one to TEAM_MAX of their agents in
|
|
2
|
+
order (a deck from the Party page, knowledge/formation.py), and the two lineups fight a deterministic relay, so everyone
|
|
3
|
+
computes the same result for the same pair and week (sharing/arena.py publishes and reads the sheets). Pure functions:
|
|
4
|
+
no clock, no global random.
|
|
4
5
|
|
|
5
6
|
A sheet holds, per coding agent, its level (1..200), tier, class (a track id or None) and six attribute levels (0..100),
|
|
6
|
-
and
|
|
7
|
-
by speed until one
|
|
7
|
+
its lineup, and its champion (the lineup's first): nothing about any lesson. Stats come from those (stats()). Each side's
|
|
8
|
+
first agent fights the other's in a bout: rounds of turns by speed until one falls or MAX_ROUNDS pass (then the one with
|
|
9
|
+
less of its HP left is out). The one left standing stays on, with the HP it has, against the next agent of the side
|
|
10
|
+
that lost one; the side that runs out of agents loses. A lineup of one against a lineup of one is a plain duel.
|
|
8
11
|
"""
|
|
9
12
|
|
|
10
13
|
from __future__ import annotations
|
|
@@ -16,13 +19,14 @@ from datetime import date
|
|
|
16
19
|
|
|
17
20
|
from ..core.agents import CODING_AGENTS
|
|
18
21
|
|
|
19
|
-
SHEET_V =
|
|
22
|
+
SHEET_V = 3 # 2: a champion; 3: a lineup (its first is the champion)
|
|
20
23
|
ATTRS = ("craft", "data", "ops", "guard", "arcana", "lore")
|
|
21
24
|
TIERS = ("novice", "adept", "expert", "master")
|
|
22
25
|
LEVEL_MAX, ATTR_MAX = 200, 100
|
|
23
|
-
|
|
26
|
+
TEAM_MAX = 3 # agents in a lineup
|
|
27
|
+
MAX_ROUNDS = 40 # a bout's
|
|
24
28
|
LOG_MAX = 400
|
|
25
|
-
HEAL_EVERY = 3 # a
|
|
29
|
+
HEAL_EVERY = 3 # a fighter with lore >= HEAL_LORE heals itself on every 3rd turn of its own
|
|
26
30
|
HEAL_LORE = 10
|
|
27
31
|
SPECIALS = {"adept": "Second wind", "expert": "Focus", "master": "Overdrive"}
|
|
28
32
|
OVERDRIVE = 2.5 # the master's special: one hit this much harder
|
|
@@ -65,6 +69,14 @@ def champion_of(agents: dict, picked) -> str:
|
|
|
65
69
|
return picked if picked in CODING_AGENTS else default_champion(agents)
|
|
66
70
|
|
|
67
71
|
|
|
72
|
+
def lineup_of(agents: dict, lineup, champion=None) -> list[str]:
|
|
73
|
+
"""`lineup` as the rules take it: distinct coding agents in its order, at most TEAM_MAX; none of them, the champion
|
|
74
|
+
alone (a sheet without a lineup)."""
|
|
75
|
+
got = list(dict.fromkeys(a for a in lineup if isinstance(a, str) and a in CODING_AGENTS))[:TEAM_MAX] \
|
|
76
|
+
if isinstance(lineup, list) else []
|
|
77
|
+
return got or [champion_of(agents, champion)]
|
|
78
|
+
|
|
79
|
+
|
|
68
80
|
def power(agents: dict) -> int:
|
|
69
81
|
"""A party's power, as the board shows it: the sum of its agents' levels."""
|
|
70
82
|
return sum(clean_agent(agents.get(n)).get("level", 1) for n in CODING_AGENTS)
|
|
@@ -82,15 +94,15 @@ def seed_for(week: str, uid_a: str, uid_b: str) -> str:
|
|
|
82
94
|
|
|
83
95
|
|
|
84
96
|
def match(uid_1: str, sheet_1: dict, uid_2: str, sheet_2: dict, week: str) -> dict:
|
|
85
|
-
"""This week's
|
|
86
|
-
so whichever of them asks, it is the same
|
|
97
|
+
"""This week's battle between two players (sheets: {"agents", "lineup", "champion"}): sides a and b are the two
|
|
98
|
+
uids sorted, so whichever of them asks, it is the same battle. Adds "a" and "b" (their uids) and "winner_uid"."""
|
|
87
99
|
(ua, sa), (ub, sb) = sorted(((uid_1, sheet_1), (uid_2, sheet_2)), key=lambda p: p[0])
|
|
88
100
|
out = battle(sa, sb, seed_for(week, ua, ub))
|
|
89
101
|
return {**out, "a": ua, "b": ub, "winner_uid": ua if out["winner"] == "a" else ub}
|
|
90
102
|
|
|
91
103
|
|
|
92
104
|
def standings(sheets: dict[str, dict], week: str) -> dict[str, dict]:
|
|
93
|
-
"""{uid: {"wins", "losses"}}: every
|
|
105
|
+
"""{uid: {"wins", "losses"}}: every lineup against every other this week (sheets: {uid: sheet})."""
|
|
94
106
|
out = {u: {"wins": 0, "losses": 0} for u in sheets}
|
|
95
107
|
uids = sorted(sheets)
|
|
96
108
|
for i, ua in enumerate(uids):
|
|
@@ -101,9 +113,9 @@ def standings(sheets: dict[str, dict], week: str) -> dict[str, dict]:
|
|
|
101
113
|
return out
|
|
102
114
|
|
|
103
115
|
|
|
104
|
-
# ------------------------------------------------------------------ the
|
|
116
|
+
# ------------------------------------------------------------------ the battle
|
|
105
117
|
def stats(agent: dict) -> dict:
|
|
106
|
-
"""A
|
|
118
|
+
"""A fighter's stats from its level and attribute levels."""
|
|
107
119
|
a = clean_agent(agent)
|
|
108
120
|
lv, at = a["level"], a["attrs"]
|
|
109
121
|
return {"hp": 20 + 4 * lv + 2 * at["guard"], "atk": 5 + at["craft"] + at["arcana"] // 2,
|
|
@@ -112,16 +124,18 @@ def stats(agent: dict) -> dict:
|
|
|
112
124
|
|
|
113
125
|
|
|
114
126
|
def battle(a_sheet: dict, b_sheet: dict, seed: str) -> dict:
|
|
115
|
-
"""`{"winner": "a"|"b", "rounds", "log", "units"}`: side a's
|
|
127
|
+
"""`{"winner": "a"|"b", "rounds", "log", "champions", "lineups", "units"}`: side a's lineup against side b's, bout
|
|
128
|
+
after bout, from `seed` (hex). Round numbers run on across bouts; a newcomer's entry is an "in" event, and a fighter
|
|
129
|
+
out of time an "out" one."""
|
|
116
130
|
rng = random.Random(int(seed, 16))
|
|
117
|
-
|
|
131
|
+
teams: dict[str, list[dict]] = {}
|
|
118
132
|
for side, sheet in (("a", a_sheet), ("b", b_sheet)):
|
|
119
133
|
agents = (sheet or {}).get("agents") or {}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
teams[side] = []
|
|
135
|
+
for i, name in enumerate(lineup_of(agents, (sheet or {}).get("lineup"), (sheet or {}).get("champion"))):
|
|
136
|
+
s = stats(agents.get(name))
|
|
137
|
+
teams[side].append({**s, "id": f"{side}:{name}", "agent": name, "order": i, "max": s["hp"], "turns": 0,
|
|
138
|
+
"special": False, "focus": False, "out": False, "key": rng.random()})
|
|
125
139
|
log: list[dict] = []
|
|
126
140
|
|
|
127
141
|
def emit(r, t, src, dst, n, hp, name=None):
|
|
@@ -139,7 +153,7 @@ def battle(a_sheet: dict, b_sheet: dict, seed: str) -> dict:
|
|
|
139
153
|
elif not dst["special"] and dst["hp"] * 2 < dst["max"] and dst["tier"] in SPECIALS:
|
|
140
154
|
special(r, dst, src)
|
|
141
155
|
|
|
142
|
-
def special(r, u, foe): # once per
|
|
156
|
+
def special(r, u, foe): # once per battle, when the fighter first drops below half its HP
|
|
143
157
|
u["special"] = True
|
|
144
158
|
name = SPECIALS[u["tier"]]
|
|
145
159
|
if u["tier"] == "adept":
|
|
@@ -152,31 +166,44 @@ def battle(a_sheet: dict, b_sheet: dict, seed: str) -> dict:
|
|
|
152
166
|
else: # one hit, OVERDRIVE times harder
|
|
153
167
|
hurt(r, u, foe, max(1, int(roll(u, foe) * OVERDRIVE * rng.uniform(0.85, 1.15))), "special", name)
|
|
154
168
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
169
|
+
down = lambda u: u["hp"] <= 0 or u["out"] # noqa: E731
|
|
170
|
+
ia = ib = rounds = 0
|
|
171
|
+
while ia < len(teams["a"]) and ib < len(teams["b"]):
|
|
172
|
+
pair = (teams["a"][ia], teams["b"][ib])
|
|
173
|
+
if rounds: # a later bout: who stepped in
|
|
174
|
+
for u in pair:
|
|
175
|
+
if not u["turns"]:
|
|
176
|
+
emit(rounds, "in", u["id"], None, 0, u["hp"])
|
|
177
|
+
for _ in range(MAX_ROUNDS):
|
|
178
|
+
rounds += 1
|
|
179
|
+
for u in sorted(pair, key=lambda x: (-x["spd"], x["key"])):
|
|
180
|
+
foe = pair[1] if u is pair[0] else pair[0]
|
|
181
|
+
if u["hp"] <= 0 or foe["hp"] <= 0:
|
|
182
|
+
continue
|
|
183
|
+
u["turns"] += 1
|
|
184
|
+
if u["heal"] and u["turns"] % HEAL_EVERY == 0 and u["hp"] < u["max"]:
|
|
185
|
+
gain = min(u["max"] - u["hp"], u["heal"])
|
|
186
|
+
u["hp"] += gain
|
|
187
|
+
emit(rounds, "heal", u["id"], u["id"], gain, u["hp"])
|
|
188
|
+
crit = rng.random() * 100 < u["crit"]
|
|
189
|
+
dmg = max(1, int(roll(u, foe) * (2 if crit else 1) * (2 if u["focus"] else 1) * rng.uniform(0.85, 1.15)))
|
|
190
|
+
u["focus"] = False
|
|
191
|
+
hurt(rounds, u, foe, dmg, "crit" if crit else "hit")
|
|
192
|
+
if pair[0]["hp"] <= 0 or pair[1]["hp"] <= 0:
|
|
193
|
+
break
|
|
194
|
+
else: # time: the one with less of its HP left is out; level with it, the seed decides
|
|
195
|
+
left = [u["hp"] / u["max"] for u in pair]
|
|
196
|
+
gone = pair[0] if left[0] < left[1] else pair[1] if left[1] < left[0] else rng.choice(pair)
|
|
197
|
+
gone["out"] = True
|
|
198
|
+
emit(rounds, "out", (pair[1] if gone is pair[0] else pair[0])["id"], gone["id"], 0, gone["hp"])
|
|
199
|
+
ia += down(pair[0])
|
|
200
|
+
ib += down(pair[1])
|
|
201
|
+
winner = "a" if ia < len(teams["a"]) else "b"
|
|
177
202
|
log.append({"r": rounds, "t": "end", "src": None, "dst": None, "n": 0, "hp": 0, "name": winner})
|
|
178
203
|
return {"winner": winner, "rounds": rounds, "log": log,
|
|
179
|
-
"champions": {s:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
204
|
+
"champions": {s: teams[s][0]["agent"] for s in "ab"},
|
|
205
|
+
"lineups": {s: [u["agent"] for u in teams[s]] for s in "ab"},
|
|
206
|
+
# end_hp: where each fighter ended, also when the log was cut short (LOG_MAX); out: beaten on time
|
|
207
|
+
"units": {u["id"]: {"hp": u["max"], "end_hp": u["hp"], "tier": u["tier"], "level": u["level"],
|
|
208
|
+
"order": u["order"], "out": u["out"]}
|
|
209
|
+
for s in "ab" for u in teams[s]}}
|