cinna-cli 0.4.2__tar.gz → 0.4.3__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.
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/PKG-INFO +22 -1
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/README.md +21 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/README.md +1 -0
- cinna_cli-0.4.3/docs/features/delegation/delegation.md +160 -0
- cinna_cli-0.4.3/docs/features/delegation/delegation_acceptance.md +250 -0
- cinna_cli-0.4.3/docs/features/delegation/delegation_tech.md +149 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/pyproject.toml +1 -1
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/client.py +44 -0
- cinna_cli-0.4.3/src/cinna/delegation.py +380 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/main.py +10 -0
- cinna_cli-0.4.3/tests/test_delegation.py +362 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/uv.lock +1 -1
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/.claude/commands/cinna-cli.feature.doc.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/.github/workflows/publish.yml +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/.gitignore +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/LICENSE.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/account_workspace/account_workspace.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/account_workspace/account_workspace_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/account_workspace/account_workspace_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_addons/agent_addons.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_addons/agent_addons_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_addons/agent_addons_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_api/agent_api.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_api/agent_api_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_api/agent_api_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_management/agent_management.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_management/agent_management_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_management/agent_management_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_schedules/agent_schedules.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_schedules/agent_schedules_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/agent_schedules/agent_schedules_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/bootstrap_onboarding/bootstrap_onboarding.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/bootstrap_onboarding/bootstrap_onboarding_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/doctor/doctor.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/doctor/doctor_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/doctor/doctor_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/git_versioning/git_versioning.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/git_versioning/git_versioning_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/git_versioning/git_versioning_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/improvement_requests/improvement_requests.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/improvement_requests/improvement_requests_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/improvement_requests/improvement_requests_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/live_sync/live_sync.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/live_sync/live_sync_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/live_sync/live_sync_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/local_agent_import/local_agent_import.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/local_agent_import/local_agent_import_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/local_agent_import/local_agent_import_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/mcp_integration/mcp_integration.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/mcp_integration/mcp_integration_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/mcp_integration/mcp_integration_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/remote_chat/remote_chat.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/remote_chat/remote_chat_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/remote_chat/remote_chat_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/remote_exec/remote_exec.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/remote_exec/remote_exec_acceptance.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/features/remote_exec/remote_exec_tech.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/interface.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/docs/mutagen_capabilities.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/scripts/check_docs_references.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/__init__.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/account.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/auth.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/bootstrap.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/chat.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/cli_version.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/config.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/console.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/context.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/doctor.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/errors.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/git_versioning.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/improve.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/kit_contract.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/local_import.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/logging.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/mcp_proxy.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/mutagen_runtime.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/scenarios.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/sync.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/sync_session.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/sync_ssh_shim.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/sync_tui.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/templates/CHAT_TESTING.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/templates/CLAUDE.md.template +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/templates/GIT_VERSIONING.md +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/src/cinna/templates/__init__.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/__init__.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/conftest.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_account.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_auth.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_bootstrap.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_chat.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_cli_version.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_client.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_config.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_console.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_context.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_doctor.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_git_versioning.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_improve.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_kit_contract.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_local_import.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_main.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_mutagen_runtime.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_onboarding.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_scenarios.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_sync.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_sync_session.py +0 -0
- {cinna_cli-0.4.2 → cinna_cli-0.4.3}/tests/test_sync_ssh_shim.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cinna-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Local development CLI for Cinna Core agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/opencinna/cinna-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/opencinna/cinna-cli
|
|
@@ -549,6 +549,27 @@ cinna chat --show 3fa85f64-5717-4562-b3fc-2c963f66afa6 # read a transcript
|
|
|
549
549
|
|
|
550
550
|
The session id is printed in the first `session` event — capture it to drive a multi-turn conversation with `--resume`, or to re-attach with `--attach`.
|
|
551
551
|
|
|
552
|
+
### `cinna delegation create | status | report | reply`
|
|
553
|
+
|
|
554
|
+
Hand work to a remote agent as a **durable delegation** — a platform task with a stable identity that survives retries and carries structured results. Run from the account workspace. Every command first checks the server's delegation capability and refuses with "This server does not support durable delegations." when it is missing or not version 1.
|
|
555
|
+
|
|
556
|
+
- `create --id KEY --target AGENT_UUID --title T --brief B [--execute]` — the identity is `--target` + `--id`: a retry with the same pair returns the original task (changed title/brief are ignored) and never launches execution twice (the backend deduplicates). Prints the backend **task id** the other commands take, and whether it executes. `--depth 2 --root ROOT` makes a sub-delegation (`--root` is required at depth 2, not allowed at depth 1); `--group` tags related work.
|
|
557
|
+
- `status TASK_ID` — one read: state, latest result, and any open question with its result id. A requester agent should end its turn and let Cinna Desktop deliver the result rather than poll.
|
|
558
|
+
- `report [TASK_ID] --status in_progress|blocked|done|failed --summary S` — `blocked` needs `--question`; `--audience user` when a human must decide (default `requester`). `--artifact` (repeatable) is a JSON object with non-empty `kind` (backend accepts `file` or `link`), `name` and an `http(s)` `ref` — upload local files separately. `--body` adds detail.
|
|
559
|
+
- **Inside a cloud task**, omit `TASK_ID`: the report goes to the current task using `AGENT_AUTH_TOKEN`, `BACKEND_URL`, `ENV_ID` and the session in `CINNA_SESSION_CONTEXT_PATH` (default `./session_context.json`); the backend checks the session belongs to this environment's agent and owner.
|
|
560
|
+
- `reply TASK_ID --result-id ID --message M` — answer a blocked question.
|
|
561
|
+
- `--json` (after the subcommand) prints `{"result": "ok", "delegation": {…}}`.
|
|
562
|
+
|
|
563
|
+
```bash
|
|
564
|
+
cinna delegation create --id research-1 --target AGENT_UUID --title Research --brief 'Find the facts' --execute
|
|
565
|
+
cinna delegation status TASK_ID
|
|
566
|
+
cinna delegation report TASK_ID --status blocked --summary 'Choose a source' --question 'Which source?' --audience user
|
|
567
|
+
cinna delegation reply TASK_ID --result-id RESULT_ID --message 'Use the primary source'
|
|
568
|
+
cinna delegation report --status done --summary 'Finished' # inside the cloud task
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Inside cloud tasks, cinna-core's MCP task server also offers a `handover_report` tool for the same report.
|
|
572
|
+
|
|
552
573
|
### `cinna dev`
|
|
553
574
|
|
|
554
575
|
Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
|
|
@@ -512,6 +512,27 @@ cinna chat --show 3fa85f64-5717-4562-b3fc-2c963f66afa6 # read a transcript
|
|
|
512
512
|
|
|
513
513
|
The session id is printed in the first `session` event — capture it to drive a multi-turn conversation with `--resume`, or to re-attach with `--attach`.
|
|
514
514
|
|
|
515
|
+
### `cinna delegation create | status | report | reply`
|
|
516
|
+
|
|
517
|
+
Hand work to a remote agent as a **durable delegation** — a platform task with a stable identity that survives retries and carries structured results. Run from the account workspace. Every command first checks the server's delegation capability and refuses with "This server does not support durable delegations." when it is missing or not version 1.
|
|
518
|
+
|
|
519
|
+
- `create --id KEY --target AGENT_UUID --title T --brief B [--execute]` — the identity is `--target` + `--id`: a retry with the same pair returns the original task (changed title/brief are ignored) and never launches execution twice (the backend deduplicates). Prints the backend **task id** the other commands take, and whether it executes. `--depth 2 --root ROOT` makes a sub-delegation (`--root` is required at depth 2, not allowed at depth 1); `--group` tags related work.
|
|
520
|
+
- `status TASK_ID` — one read: state, latest result, and any open question with its result id. A requester agent should end its turn and let Cinna Desktop deliver the result rather than poll.
|
|
521
|
+
- `report [TASK_ID] --status in_progress|blocked|done|failed --summary S` — `blocked` needs `--question`; `--audience user` when a human must decide (default `requester`). `--artifact` (repeatable) is a JSON object with non-empty `kind` (backend accepts `file` or `link`), `name` and an `http(s)` `ref` — upload local files separately. `--body` adds detail.
|
|
522
|
+
- **Inside a cloud task**, omit `TASK_ID`: the report goes to the current task using `AGENT_AUTH_TOKEN`, `BACKEND_URL`, `ENV_ID` and the session in `CINNA_SESSION_CONTEXT_PATH` (default `./session_context.json`); the backend checks the session belongs to this environment's agent and owner.
|
|
523
|
+
- `reply TASK_ID --result-id ID --message M` — answer a blocked question.
|
|
524
|
+
- `--json` (after the subcommand) prints `{"result": "ok", "delegation": {…}}`.
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
cinna delegation create --id research-1 --target AGENT_UUID --title Research --brief 'Find the facts' --execute
|
|
528
|
+
cinna delegation status TASK_ID
|
|
529
|
+
cinna delegation report TASK_ID --status blocked --summary 'Choose a source' --question 'Which source?' --audience user
|
|
530
|
+
cinna delegation reply TASK_ID --result-id RESULT_ID --message 'Use the primary source'
|
|
531
|
+
cinna delegation report --status done --summary 'Finished' # inside the cloud task
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Inside cloud tasks, cinna-core's MCP task server also offers a `handover_report` tool for the same report.
|
|
535
|
+
|
|
515
536
|
### `cinna dev`
|
|
516
537
|
|
|
517
538
|
Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
|
|
@@ -296,6 +296,7 @@ authoring convention.
|
|
|
296
296
|
| **Agent addons** | `cinna skills` (list — with each declared credential slot's readiness, publish, install, uninstall, update, toggle, refresh, catalog, show, revisions, files, grants, grant, revoke, visibility, delist, relist) — the whole skills lifecycle: what an agent carries, publishing one of its `skills/<name>/` folders to the catalog, installing a package on another agent and keeping it current, and sharing after the fact | [business](features/agent_addons/agent_addons.md) · [tech](features/agent_addons/agent_addons_tech.md) · [acceptance](features/agent_addons/agent_addons_acceptance.md) |
|
|
297
297
|
| **MCP integration** | `cinna connect mcp` · `cinna mcp-proxy` (knowledge stdio server) | [business](features/mcp_integration/mcp_integration.md) · [tech](features/mcp_integration/mcp_integration_tech.md) · [acceptance](features/mcp_integration/mcp_integration_acceptance.md) |
|
|
298
298
|
| **Git versioning** | `cinna git` (link, status, commit, push, pull, log, checkout, unlink) | [business](features/git_versioning/git_versioning.md) · [tech](features/git_versioning/git_versioning_tech.md) · [acceptance](features/git_versioning/git_versioning_acceptance.md) |
|
|
299
|
+
| **Durable delegation** | `cinna delegation` (create — idempotent per target + `--id`, status, report — owner side with a task id or from inside a cloud task, reply), gated on the backend capability version | [business](features/delegation/delegation.md) · [tech](features/delegation/delegation_tech.md) · [acceptance](features/delegation/delegation_acceptance.md) |
|
|
299
300
|
| **Improvement requests** | `cinna improve` (list, show, download, status) | [business](features/improvement_requests/improvement_requests.md) · [tech](features/improvement_requests/improvement_requests_tech.md) · [acceptance](features/improvement_requests/improvement_requests_acceptance.md) |
|
|
300
301
|
| **Local agent import** | `cinna agent import` (Local Agent Kit → cloud agent), incl. the versioned folder contract (`.cinna-kit/layout.json`), `publications.json` and `content_hash` | [business](features/local_agent_import/local_agent_import.md) · [tech](features/local_agent_import/local_agent_import_tech.md) · [acceptance](features/local_agent_import/local_agent_import_acceptance.md) |
|
|
301
302
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Durable Delegation (`cinna delegation`)
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Let a requester (a developer, a local coding agent, or a script) hand a piece of
|
|
6
|
+
work to a remote platform agent as a **durable delegation**: a platform task
|
|
7
|
+
with a stable identity that survives retries, reports structured results back,
|
|
8
|
+
and can ask the requester (or a human) a question and wait for the answer.
|
|
9
|
+
The same command group lets the cloud agent doing the work report its own
|
|
10
|
+
progress from inside its task session.
|
|
11
|
+
|
|
12
|
+
## Mental model / core concepts
|
|
13
|
+
|
|
14
|
+
A delegation is **not** local state. It is a platform task (the same task
|
|
15
|
+
record the platform UI shows) carrying extra delegation metadata. The CLI is a
|
|
16
|
+
remote control over it; nothing is written to the workspace.
|
|
17
|
+
|
|
18
|
+
- **Requester key (`--id`)** — a name the requester picks for one piece of work
|
|
19
|
+
(`research-1`). Together with the target agent it forms the delegation's
|
|
20
|
+
identity. Reusing the same key against the same target means "the same work",
|
|
21
|
+
never "new work".
|
|
22
|
+
- **Target** — the UUID of the remote agent that will do the work.
|
|
23
|
+
- **Task id** — the backend id of the created task. `create` prints it; every
|
|
24
|
+
other command (`status`, `report`, `reply`) takes it.
|
|
25
|
+
- **Result** — a structured report on the task: a status (`in_progress`,
|
|
26
|
+
`blocked`, `done`, `failed`), a summary, an optional body, optional
|
|
27
|
+
artifacts, and — when blocked — a question with an audience.
|
|
28
|
+
- **Question / reply** — a `blocked` result carries a question. Its result id
|
|
29
|
+
is the handle the answer is sent against (`reply --result-id`).
|
|
30
|
+
- **Audience** — who must answer a question: `requester` (the agent/tool that
|
|
31
|
+
created the delegation) or `user` (a human decision).
|
|
32
|
+
- **Depth and root** — a delegation may itself delegate once more. Depth 1 is a
|
|
33
|
+
top-level delegation whose root is itself; depth 2 is a sub-delegation that
|
|
34
|
+
names its depth-1 root. Deeper chains are not allowed.
|
|
35
|
+
- **Owner side vs executor side** —
|
|
36
|
+
- *Owner side*: run from an **account workspace**, authenticated by the
|
|
37
|
+
account token, reaching the platform through the account API proxy
|
|
38
|
+
(`create`, `status`, `reply`, and `report` with a task id).
|
|
39
|
+
- *Executor side*: run **inside a cloud task session** in the agent's
|
|
40
|
+
environment, authenticated by the environment's own agent token
|
|
41
|
+
(`report` without a task id). It reports on "the task I am currently
|
|
42
|
+
running" — it never needs to know the task id.
|
|
43
|
+
- **Capability negotiation** — durable delegations are a versioned backend
|
|
44
|
+
extension. Every owner-side command first asks the server whether it supports
|
|
45
|
+
version 1; if not, the command refuses instead of calling routes that may not
|
|
46
|
+
exist.
|
|
47
|
+
|
|
48
|
+
## User flows
|
|
49
|
+
|
|
50
|
+
### Hand work to a remote agent
|
|
51
|
+
1. From the account workspace run
|
|
52
|
+
`cinna delegation create --id research-1 --target <agent uuid> --title … --brief …`,
|
|
53
|
+
adding `--execute` to start the work immediately.
|
|
54
|
+
2. The CLI prints the backend task id and whether the task will execute.
|
|
55
|
+
3. On a network error or timeout, run the **same command again**: the platform
|
|
56
|
+
returns the original task. Any changed `--title` / `--brief` on the retry is
|
|
57
|
+
ignored, and execution is never launched a second time.
|
|
58
|
+
|
|
59
|
+
### Follow up without polling
|
|
60
|
+
4. `cinna delegation status <task id>` reads the task once: its state, the
|
|
61
|
+
latest result, and any open question together with that question's result id.
|
|
62
|
+
5. A requester agent should **end its turn** after delegating and let Cinna
|
|
63
|
+
Desktop deliver the result, rather than polling `status` in a loop.
|
|
64
|
+
|
|
65
|
+
### Report progress (owner side)
|
|
66
|
+
6. `cinna delegation report <task id> --status in_progress --summary …` records a
|
|
67
|
+
result on the task through the account workspace.
|
|
68
|
+
|
|
69
|
+
### Report progress (inside the cloud task)
|
|
70
|
+
7. The agent executing the task runs
|
|
71
|
+
`cinna delegation report --status done --summary … [--artifact …]` with no task
|
|
72
|
+
id. The CLI uses the environment's agent credentials and the current session
|
|
73
|
+
context; the backend attaches the result to the task that session belongs to.
|
|
74
|
+
|
|
75
|
+
### Ask and answer a question
|
|
76
|
+
8. The executor reports `--status blocked --question "Which source?"`, with
|
|
77
|
+
`--audience user` when a human must decide.
|
|
78
|
+
9. The requester reads the question and its result id with `status`, then
|
|
79
|
+
answers with `cinna delegation reply <task id> --result-id <id> --message …`.
|
|
80
|
+
|
|
81
|
+
### Machine-readable use
|
|
82
|
+
10. Every command accepts `--json` after the subcommand; the output is a single
|
|
83
|
+
`{"result": "ok", "delegation": …}` line carrying the backend's answer, or
|
|
84
|
+
the standard `{"result": "error", …}` line on failure.
|
|
85
|
+
|
|
86
|
+
## Business rules
|
|
87
|
+
|
|
88
|
+
- **Account workspace required** for every owner-side command; outside one they
|
|
89
|
+
fail loud.
|
|
90
|
+
- **Capability gate** — if the server lacks the capability route, or reports a
|
|
91
|
+
version other than 1, the command stops with "This server does not support
|
|
92
|
+
durable delegations." and sends nothing else.
|
|
93
|
+
- **Retry identity** — the delegation identity is derived only from
|
|
94
|
+
target + requester key. The same pair always yields the same identity, so a
|
|
95
|
+
retry is idempotent. Idempotency **depends on the backend** deduplicating on
|
|
96
|
+
that identity; the CLI keeps no local record of what it created.
|
|
97
|
+
- **First write wins** — on a retry the original task is returned unchanged;
|
|
98
|
+
new title/brief text does not update it.
|
|
99
|
+
- **Execute at most once** — `--execute` only takes effect when the task is
|
|
100
|
+
newly created. A retry with `--execute` never launches a second run.
|
|
101
|
+
- **Depth and root** — depth is 1 or 2. At depth 1 `--root` is forbidden (the
|
|
102
|
+
delegation is its own root). At depth 2 `--root` is required.
|
|
103
|
+
- **Origin** — CLI-created delegations are marked as originating externally
|
|
104
|
+
(not from a platform agent, chat, or task).
|
|
105
|
+
- **Blocked needs a question** — `--status blocked` without `--question` is
|
|
106
|
+
refused before any request.
|
|
107
|
+
- **Audience** — defaults to `requester`; use `user` when a human must decide.
|
|
108
|
+
- **Artifacts are references, not uploads** — each `--artifact` is a JSON object
|
|
109
|
+
with a non-empty `kind`, a non-empty `name`, and an `http(s)` `ref`. The CLI
|
|
110
|
+
checks only that shape; the backend further restricts `kind` (currently
|
|
111
|
+
`file` or `link`). Local files must be uploaded separately first; the CLI
|
|
112
|
+
never uploads them.
|
|
113
|
+
- **Executor report is session-bound** — without a task id, `report` needs the
|
|
114
|
+
environment's agent token, backend URL and environment id, plus the current
|
|
115
|
+
session context. Missing any of them is a hard failure. The backend verifies
|
|
116
|
+
that the session belongs to this environment's agent and owner, so an agent
|
|
117
|
+
cannot report on someone else's task.
|
|
118
|
+
- **Status is a single read** — no watching, no waiting.
|
|
119
|
+
- **Fail loud** — any backend refusal surfaces as a platform error with the
|
|
120
|
+
backend's own detail; nothing is retried silently.
|
|
121
|
+
|
|
122
|
+
## Architecture overview
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
Owner side (account workspace)
|
|
126
|
+
cinna delegation create|status|reply|report TASK_ID
|
|
127
|
+
│
|
|
128
|
+
▼
|
|
129
|
+
delegation.py ──► AccountClient ──► POST /api/v1/cli/account/api-proxy
|
|
130
|
+
│ 1. GET tasks/delegation-capabilities (version 1?)
|
|
131
|
+
│ 2. the delegation route
|
|
132
|
+
▼
|
|
133
|
+
cinna-core tasks API (dedup, execution, results)
|
|
134
|
+
|
|
135
|
+
Executor side (inside a cloud task session)
|
|
136
|
+
cinna delegation report (no TASK_ID)
|
|
137
|
+
│ env: AGENT_AUTH_TOKEN, BACKEND_URL, ENV_ID + session_context.json
|
|
138
|
+
▼
|
|
139
|
+
POST {BACKEND_URL}/api/v1/agent/tasks/current/delegation-result
|
|
140
|
+
│ backend checks session ↔ environment agent ↔ owner
|
|
141
|
+
▼
|
|
142
|
+
result attached to the current task ──► delivered to the requester (Desktop)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Integration points
|
|
146
|
+
|
|
147
|
+
- **Account workspace** — owner-side commands use the account token and the api
|
|
148
|
+
proxy. See [Account Workspace](../account_workspace/account_workspace.md).
|
|
149
|
+
- **Agent API / `cinna api`** — the same api-proxy escape hatch `cinna api`
|
|
150
|
+
exposes; delegation wraps the task routes with identity, validation and a
|
|
151
|
+
capability gate. See [Agent API](../agent_api/agent_api.md).
|
|
152
|
+
- **Remote chat** — a delegated task runs as a platform session like the ones
|
|
153
|
+
`cinna chat` drives. See [Remote Chat](../remote_chat/remote_chat.md).
|
|
154
|
+
- **cinna-core task MCP server (not in this repo)** — inside cloud tasks the
|
|
155
|
+
backend's MCP task server exposes a `handover_report` tool that records the
|
|
156
|
+
same kind of result. It is implemented and documented in cinna-core; this CLI
|
|
157
|
+
only offers the equivalent command-line path.
|
|
158
|
+
|
|
159
|
+
Implementation: see [delegation_tech.md](delegation_tech.md).
|
|
160
|
+
Real-usage e2e scenarios: see [delegation_acceptance.md](delegation_acceptance.md).
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Durable Delegation — Acceptance Scenarios (live e2e)
|
|
2
|
+
|
|
3
|
+
The catalog of **real-usage scenarios** for an agent integration-testing
|
|
4
|
+
`cinna delegation` against a **live** platform: a real cinna-core backend with
|
|
5
|
+
the durable-delegation extension, a real account workspace, and at least one
|
|
6
|
+
real target agent. These exercise backend dedup, task execution, the result /
|
|
7
|
+
question / reply cycle, and the executor-side report from inside a cloud task —
|
|
8
|
+
the parts unit tests only mock.
|
|
9
|
+
|
|
10
|
+
How to use: run the **Steps** from inside the account workspace (except where a
|
|
11
|
+
scenario says "inside the cloud task"), assert the **Expected**, and check the
|
|
12
|
+
**Watch for** items. Scenarios 2–3 (retry identity) and 9–11 (executor report)
|
|
13
|
+
are the highest-value ones for any change to `src/cinna/delegation.py`.
|
|
14
|
+
|
|
15
|
+
## Preconditions
|
|
16
|
+
|
|
17
|
+
- A reachable cinna-core backend whose `tasks/delegation-capabilities` returns
|
|
18
|
+
`version: 1`; ideally also an older backend without it (scenario 13).
|
|
19
|
+
- An **account workspace** (`.cinna/account.json` reachable from the cwd;
|
|
20
|
+
`cinna login` if the token expired).
|
|
21
|
+
- **Editable install**: `which cinna` resolves, and
|
|
22
|
+
`python3 -c "import cinna,os;print(os.path.dirname(cinna.__file__))"` points at
|
|
23
|
+
this repo's `src/cinna`. `cinna delegation --help` lists create, status,
|
|
24
|
+
report, reply.
|
|
25
|
+
- At least one **target agent** UUID (`cinna account agents`), with a running
|
|
26
|
+
or startable environment.
|
|
27
|
+
- `jq` for asserting JSON output.
|
|
28
|
+
- Use a fresh key per run, e.g. `KEY=acc-$(date +%s)`, so earlier runs do not
|
|
29
|
+
dedup into this one.
|
|
30
|
+
|
|
31
|
+
## Scenario catalog
|
|
32
|
+
|
|
33
|
+
### 1. Create without executing
|
|
34
|
+
|
|
35
|
+
- **Goal:** register work without starting it.
|
|
36
|
+
- **Steps:**
|
|
37
|
+
```
|
|
38
|
+
cinna delegation create --id "$KEY" --target "$AGENT" --title Research --brief 'Find the facts'
|
|
39
|
+
```
|
|
40
|
+
- **Expected:** exit 0; output shows the backend task id and that it will
|
|
41
|
+
**not** execute. The task appears in the platform UI for the target agent,
|
|
42
|
+
not started. Record the id as `TASK`.
|
|
43
|
+
- **Watch for:** the delegation id / requester key printed instead of the
|
|
44
|
+
backend task id; the task auto-starting.
|
|
45
|
+
|
|
46
|
+
### 2. Retry reuses the original task (idempotent create)
|
|
47
|
+
|
|
48
|
+
- **Goal:** a retry after a timeout never duplicates work.
|
|
49
|
+
- **Steps:**
|
|
50
|
+
```
|
|
51
|
+
cinna delegation create --id "$KEY" --target "$AGENT" --title Research --brief 'Find the facts' --json | jq -r .delegation.id
|
|
52
|
+
cinna delegation create --id "$KEY" --target "$AGENT" --title 'Changed title' --brief 'Changed brief' --json | jq -r .delegation.id
|
|
53
|
+
```
|
|
54
|
+
- **Expected:** both ids equal `TASK`. The platform shows one task, still titled
|
|
55
|
+
`Research` with the original brief.
|
|
56
|
+
- **Watch for:** a second task; the title/brief being overwritten; a non-`ok`
|
|
57
|
+
result on the retry.
|
|
58
|
+
|
|
59
|
+
### 3. `--execute` runs at most once
|
|
60
|
+
|
|
61
|
+
- **Goal:** execution is never launched twice for the same key.
|
|
62
|
+
- **Steps:**
|
|
63
|
+
```
|
|
64
|
+
KEY2=$KEY-exec
|
|
65
|
+
cinna delegation create --id "$KEY2" --target "$AGENT" --title Run --brief 'Say hello' --execute
|
|
66
|
+
cinna delegation create --id "$KEY2" --target "$AGENT" --title Run --brief 'Say hello' --execute
|
|
67
|
+
```
|
|
68
|
+
- **Expected:** the first reports the task executes; the second returns the same
|
|
69
|
+
task id. The platform shows exactly one execution/session for that task.
|
|
70
|
+
- **Watch for:** two sessions on the target agent; the retry reporting a new
|
|
71
|
+
execution.
|
|
72
|
+
|
|
73
|
+
### 4. Same key, different target is different work
|
|
74
|
+
|
|
75
|
+
- **Goal:** identity is target + key.
|
|
76
|
+
- **Steps:** repeat scenario 1's command with `--target "$AGENT_B"` (a second
|
|
77
|
+
agent) and the same `$KEY`.
|
|
78
|
+
- **Expected:** a new, different task id.
|
|
79
|
+
- **Watch for:** dedup across targets (identity must include the target).
|
|
80
|
+
|
|
81
|
+
### 5. Depth and root rules
|
|
82
|
+
|
|
83
|
+
- **Goal:** sub-delegation shape is enforced client-side.
|
|
84
|
+
- **Steps:**
|
|
85
|
+
```
|
|
86
|
+
cinna delegation create --id "$KEY-d2" --target "$AGENT" --title T --brief B --depth 2
|
|
87
|
+
cinna delegation create --id "$KEY-d1" --target "$AGENT" --title T --brief B --root "$ROOT_ID"
|
|
88
|
+
cinna delegation create --id "$KEY-d3" --target "$AGENT" --title T --brief B --depth 3
|
|
89
|
+
cinna delegation create --id "$KEY-d2" --target "$AGENT" --title T --brief B --depth 2 --root "$ROOT_ID" --json
|
|
90
|
+
```
|
|
91
|
+
(`$ROOT_ID` is the `delegation_metadata.id` of the task from scenario 1.)
|
|
92
|
+
- **Expected:** the first three fail with a usage error (exit 2) and create
|
|
93
|
+
nothing; the last succeeds and its `delegation_metadata` has `depth: 2`,
|
|
94
|
+
`root: $ROOT_ID`. A depth-1 task's metadata has `root` equal to its own id.
|
|
95
|
+
- **Watch for:** a task created by an invalid invocation; `root` null at depth 1.
|
|
96
|
+
|
|
97
|
+
### 6. Status is a single read
|
|
98
|
+
|
|
99
|
+
- **Goal:** read state, latest result and open question once.
|
|
100
|
+
- **Steps:**
|
|
101
|
+
```
|
|
102
|
+
cinna delegation status "$TASK"
|
|
103
|
+
cinna delegation status "$TASK" --json | jq .result
|
|
104
|
+
```
|
|
105
|
+
- **Expected:** human output shows state, latest result (or none), and any open
|
|
106
|
+
question with its result id; returns immediately. JSON prints `"ok"` with the
|
|
107
|
+
task detail under `.delegation`.
|
|
108
|
+
- **Watch for:** the command waiting or polling; missing result id next to an
|
|
109
|
+
open question.
|
|
110
|
+
|
|
111
|
+
### 7. Owner-side report and blocked question
|
|
112
|
+
|
|
113
|
+
- **Goal:** record results and ask a question through the account workspace.
|
|
114
|
+
- **Steps:**
|
|
115
|
+
```
|
|
116
|
+
cinna delegation report "$TASK" --status in_progress --summary 'Started'
|
|
117
|
+
cinna delegation report "$TASK" --status blocked --summary 'Need input'
|
|
118
|
+
cinna delegation report "$TASK" --status blocked --summary 'Need input' --question 'Which source?' --audience user
|
|
119
|
+
cinna delegation status "$TASK"
|
|
120
|
+
```
|
|
121
|
+
- **Expected:** the first succeeds; the second fails client-side (blocked
|
|
122
|
+
without a question) and sends nothing; the third succeeds. `status` shows the
|
|
123
|
+
open question `Which source?`, audience `user`, and its result id (`RESULT`).
|
|
124
|
+
- **Watch for:** a blocked result accepted without a question; audience
|
|
125
|
+
defaulting wrongly (default is `requester`).
|
|
126
|
+
|
|
127
|
+
### 8. Reply answers the question
|
|
128
|
+
|
|
129
|
+
- **Goal:** close the question loop.
|
|
130
|
+
- **Steps:**
|
|
131
|
+
```
|
|
132
|
+
cinna delegation reply "$TASK" --result-id "$RESULT" --message 'Use the primary source'
|
|
133
|
+
cinna delegation status "$TASK"
|
|
134
|
+
```
|
|
135
|
+
- **Expected:** exit 0; the question is no longer open and the reply is visible
|
|
136
|
+
on the task. A wrong `--result-id` yields a platform error with the backend
|
|
137
|
+
detail (exit 1).
|
|
138
|
+
- **Watch for:** reply accepted against a non-question result id.
|
|
139
|
+
|
|
140
|
+
### 9. Artifacts are validated
|
|
141
|
+
|
|
142
|
+
- **Goal:** only well-formed, portable artifact references are sent.
|
|
143
|
+
- **Steps:**
|
|
144
|
+
```
|
|
145
|
+
cinna delegation report "$TASK" --status done --summary Done --artifact '{"kind":"link","name":"r","ref":"https://example.com/r.pdf"}'
|
|
146
|
+
cinna delegation report "$TASK" --status done --summary Done --artifact 'not json'
|
|
147
|
+
cinna delegation report "$TASK" --status done --summary Done --artifact '{"kind":"link","name":"r","ref":"file:///tmp/r.pdf"}'
|
|
148
|
+
cinna delegation report "$TASK" --status done --summary Done --artifact '{"kind":"","name":"r","ref":"https://x"}'
|
|
149
|
+
cinna delegation report "$TASK" --status done --summary Done --artifact '["a"]'
|
|
150
|
+
```
|
|
151
|
+
- **Expected:** only the first succeeds and the artifact appears on the latest
|
|
152
|
+
result. The others fail client-side (exit 2) with no request sent.
|
|
153
|
+
- **Watch for:** local paths or non-http refs accepted; empty `kind`/`name`
|
|
154
|
+
accepted; a JSON array accepted as an artifact.
|
|
155
|
+
|
|
156
|
+
### 10. Executor report from inside the cloud task
|
|
157
|
+
|
|
158
|
+
- **Goal:** the working agent reports on its own current task without a task id.
|
|
159
|
+
- **Setup:** a task created with `--execute` (scenario 3), running in the target
|
|
160
|
+
environment where `AGENT_AUTH_TOKEN`, `BACKEND_URL`, `ENV_ID` are set and
|
|
161
|
+
`session_context.json` holds `backend_session_id`.
|
|
162
|
+
- **Steps (inside the cloud task, e.g. as an instruction in the brief):**
|
|
163
|
+
```
|
|
164
|
+
cinna delegation report --status done --summary 'Finished' --json
|
|
165
|
+
```
|
|
166
|
+
Then, from the account workspace: `cinna delegation status "$TASK3"`.
|
|
167
|
+
- **Expected:** `{"result": "ok", "delegation": {…}}`; `status` shows the `done`
|
|
168
|
+
result on that task. The request went to
|
|
169
|
+
`$BACKEND_URL/api/v1/agent/tasks/current/delegation-result` with the bearer
|
|
170
|
+
token and `X-Agent-Env-Id`.
|
|
171
|
+
- **Watch for:** the result landing on a different task; the command trying to
|
|
172
|
+
load an account workspace.
|
|
173
|
+
|
|
174
|
+
### 11. Executor report fails loud without its context
|
|
175
|
+
|
|
176
|
+
- **Goal:** no silent fallback when not in a cloud task.
|
|
177
|
+
- **Steps (from a plain shell, then with a partial env):**
|
|
178
|
+
```
|
|
179
|
+
env -u AGENT_AUTH_TOKEN -u BACKEND_URL -u ENV_ID cinna delegation report --status done --summary x
|
|
180
|
+
AGENT_AUTH_TOKEN=t BACKEND_URL=https://backend.example ENV_ID=e CINNA_SESSION_CONTEXT_PATH=/nonexistent.json cinna delegation report --status done --summary x
|
|
181
|
+
```
|
|
182
|
+
- **Expected:** both fail before any network call with a clear message (missing
|
|
183
|
+
environment / no current session). Nothing is recorded on any task.
|
|
184
|
+
- **Watch for:** falling back to the account workspace; a request sent with an
|
|
185
|
+
empty `X-Agent-Env-Id` or no `source_session_id`.
|
|
186
|
+
|
|
187
|
+
### 12. Executor report rejected by the backend
|
|
188
|
+
|
|
189
|
+
- **Goal:** backend verification and error surfacing.
|
|
190
|
+
- **Steps (inside a cloud environment):** point `CINNA_SESSION_CONTEXT_PATH` at
|
|
191
|
+
a file whose `backend_session_id` belongs to another agent's session, then run
|
|
192
|
+
`cinna delegation report --status done --summary x --json`.
|
|
193
|
+
- **Expected:** the backend refuses; the CLI prints
|
|
194
|
+
`{"result": "error", "code": "platform_error", "http_status": 4xx, …}` and
|
|
195
|
+
exits 1 (5xx → `platform_unavailable`, exit 12). A 3xx is not followed and is
|
|
196
|
+
also an error.
|
|
197
|
+
- **Watch for:** the report accepted for a foreign session; a redirect followed
|
|
198
|
+
with the bearer token.
|
|
199
|
+
|
|
200
|
+
### 13. Unsupported server
|
|
201
|
+
|
|
202
|
+
- **Goal:** capability gate on every owner-side verb.
|
|
203
|
+
- **Steps (against a backend without the extension, or one reporting another
|
|
204
|
+
version):**
|
|
205
|
+
```
|
|
206
|
+
cinna delegation create --id "$KEY" --target "$AGENT" --title T --brief B
|
|
207
|
+
cinna delegation status "$TASK"
|
|
208
|
+
cinna delegation report "$TASK" --status done --summary x
|
|
209
|
+
cinna delegation reply "$TASK" --result-id r --message m
|
|
210
|
+
```
|
|
211
|
+
- **Expected:** each fails with "This server does not support durable
|
|
212
|
+
delegations." and no task is created or changed.
|
|
213
|
+
- **Watch for:** a create going through on an old backend (which would ignore
|
|
214
|
+
`external_ref` and break retry safety).
|
|
215
|
+
|
|
216
|
+
### 14. `--json` is per-command
|
|
217
|
+
|
|
218
|
+
- **Goal:** machine output works after the subcommand.
|
|
219
|
+
- **Steps:**
|
|
220
|
+
```
|
|
221
|
+
cinna delegation status "$TASK" --json
|
|
222
|
+
cinna delegation create --id "$KEY" --target "$AGENT" --title T --brief B --json
|
|
223
|
+
```
|
|
224
|
+
- **Expected:** each prints exactly one JSON line with `result: "ok"` and the
|
|
225
|
+
backend body under `delegation`; no Rich output on stdout.
|
|
226
|
+
- **Watch for:** extra human lines mixed into stdout; the body at top level
|
|
227
|
+
instead of under `delegation`.
|
|
228
|
+
|
|
229
|
+
### 15. Outside an account workspace
|
|
230
|
+
|
|
231
|
+
- **Steps:** `cd /tmp && cinna delegation status "$TASK"`
|
|
232
|
+
- **Expected:** fails loud pointing at the missing account workspace; no request.
|
|
233
|
+
|
|
234
|
+
## Cross-cutting invariants
|
|
235
|
+
|
|
236
|
+
- One target + key ⇒ one task and at most one execution, across any number of
|
|
237
|
+
retries and any changed title/brief.
|
|
238
|
+
- Every owner-side verb calls the capability route first; an unsupported server
|
|
239
|
+
receives no task-route writes.
|
|
240
|
+
- Client-side validation failures (depth/root, blocked without question,
|
|
241
|
+
artifacts, missing executor env) send **no** request.
|
|
242
|
+
- The executor bearer token is only ever sent to `BACKEND_URL` (no redirects).
|
|
243
|
+
- Nothing is written to the workspace, `.cinna/`, or `~/.cinna/agents.json`. <!-- nocheck -->
|
|
244
|
+
- Every `--json` invocation ends with exactly one `{"result": …}` line.
|
|
245
|
+
|
|
246
|
+
## Cleanup
|
|
247
|
+
|
|
248
|
+
- Delete or archive the test tasks (keys prefixed `acc-`) in the platform UI, or
|
|
249
|
+
via `cinna api DELETE tasks/<id>` where the backend allows it.
|
|
250
|
+
- Stop any sessions left running on the target agent by `--execute` scenarios.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Durable Delegation — Technical Reference
|
|
2
|
+
|
|
3
|
+
Implementation of [delegation.md](delegation.md). cinna-cli is a Python CLI; all
|
|
4
|
+
logic lives in `src/cinna/`, tests in `tests/`. The `cinna delegation` group is a
|
|
5
|
+
thin validation + transport layer over cinna-core task routes. It keeps no local
|
|
6
|
+
state.
|
|
7
|
+
|
|
8
|
+
## File locations
|
|
9
|
+
|
|
10
|
+
- `src/cinna/delegation.py` — the whole feature: the Click group, the four verb
|
|
11
|
+
commands, the owner-side request helper (capability gate + proxy call), the
|
|
12
|
+
executor-side report path, and output rendering.
|
|
13
|
+
- `src/cinna/main.py` — attaches `json_option()` to every delegation
|
|
14
|
+
subcommand and registers the group on the root CLI
|
|
15
|
+
(`cli.add_command(delegation)`).
|
|
16
|
+
- `src/cinna/account.py` — `find_account_root()` / `load_account_config()`:
|
|
17
|
+
locate the account workspace and its token.
|
|
18
|
+
- `src/cinna/client.py` — public wrappers `get_delegation_capabilities()`,
|
|
19
|
+
`create_delegated_task()`, `get_task_detail()`, `put_delegation_result()`,
|
|
20
|
+
`post_delegation_reply()` over `_proxy_json()` / `api_proxy()` (the api-proxy
|
|
21
|
+
transport and error mapping), plus `AccountClient.error_detail()` used by the
|
|
22
|
+
executor path.
|
|
23
|
+
- `src/cinna/console.py` — `emit_result()` (the `--json` success line) and
|
|
24
|
+
`json_mode`.
|
|
25
|
+
- `src/cinna/errors.py` — `PlatformError`, `CinnaExit` (exit codes and the
|
|
26
|
+
`--json` error line).
|
|
27
|
+
- Tests: `tests/test_delegation.py` — retry identity and payload, depth/root
|
|
28
|
+
rules, owner-side routes, capability gate, report validation (blocked
|
|
29
|
+
question, artifacts), executor report (env, session context, headers,
|
|
30
|
+
redirects, non-2xx), and the `--json` envelope.
|
|
31
|
+
|
|
32
|
+
## Command surface
|
|
33
|
+
|
|
34
|
+
- `cinna delegation` → `src/cinna/delegation.py:delegation()` (Click group).
|
|
35
|
+
- `cinna delegation create` → `src/cinna/delegation.py:create()`.
|
|
36
|
+
- `cinna delegation status` → `src/cinna/delegation.py:status()`.
|
|
37
|
+
- `cinna delegation report` → `src/cinna/delegation.py:report()`.
|
|
38
|
+
- `cinna delegation reply` → `src/cinna/delegation.py:reply()`.
|
|
39
|
+
|
|
40
|
+
Each verb carries its own `--json` option (the shared per-command
|
|
41
|
+
`src/cinna/main.py:json_option()`, applied in `main.py` at registration because
|
|
42
|
+
`delegation.py` cannot import `main.py` without a cycle); `--json` is not taken
|
|
43
|
+
from the root group.
|
|
44
|
+
|
|
45
|
+
## Key functions & flow
|
|
46
|
+
|
|
47
|
+
- **Owner-side request helper** (`src/cinna/delegation.py`) — every owner-side
|
|
48
|
+
call: `find_account_root()` + `load_account_config()`, open an
|
|
49
|
+
`AccountClient`, `get_delegation_capabilities()`. A 404/405 or a body that is
|
|
50
|
+
not a dict with `version` `1` raises "This server does not support durable
|
|
51
|
+
delegations."; other platform errors propagate. Only then is the actual route
|
|
52
|
+
called through the matching public client method.
|
|
53
|
+
- `src/cinna/delegation.py:create()` — builds the identity string as the JSON
|
|
54
|
+
array `[target, key]`; `external_ref` is its sha256 hex digest; the delegation
|
|
55
|
+
id is `uuid5(NAMESPACE_URL, identity)`. Validates depth/root before any call.
|
|
56
|
+
POSTs `tasks/` with `title`, `original_message` (= `--brief`),
|
|
57
|
+
`selected_agent_id` (= `--target`), `external_ref`, `delegation_metadata`, and
|
|
58
|
+
`auto_execute` (= `--execute`). Human output: the backend task id and whether
|
|
59
|
+
it executes.
|
|
60
|
+
- `src/cinna/delegation.py:status()` — one `GET tasks/{id}/detail`; human output
|
|
61
|
+
shows state, the latest result, and any open question with its result id.
|
|
62
|
+
- `src/cinna/delegation.py:report()` — validates (`blocked` ⇒ `--question`;
|
|
63
|
+
each `--artifact` parses to a JSON object with non-empty `kind`, `name` and an
|
|
64
|
+
`http`/`https` `ref`), builds the payload (`status`, `summary`, `question`,
|
|
65
|
+
`audience`, `artifacts`, `body`), then:
|
|
66
|
+
- with `TASK_ID`: owner-side `PUT tasks/{id}/delegation-result`;
|
|
67
|
+
- without: the executor path (below).
|
|
68
|
+
- **Executor path** (`report()` without `TASK_ID`) — reads `AGENT_AUTH_TOKEN`,
|
|
69
|
+
`BACKEND_URL`, `ENV_ID` (all required); reads `backend_session_id` from the
|
|
70
|
+
file at `CINNA_SESSION_CONTEXT_PATH` (default `session_context.json` in the
|
|
71
|
+
cwd) and adds it as `source_session_id`; POSTs directly (not via the account
|
|
72
|
+
proxy) with `Authorization: Bearer <token>` and `X-Agent-Env-Id: <ENV_ID>`,
|
|
73
|
+
redirects disabled, 30 s timeout. No capability gate on this path. Any 2xx is
|
|
74
|
+
accepted (an empty or non-JSON success body is treated as `{}` so callers do
|
|
75
|
+
not retry an accepted report); non-2xx raises `PlatformError` with the detail
|
|
76
|
+
from `AccountClient.error_detail()`.
|
|
77
|
+
- `src/cinna/delegation.py:reply()` — `POST tasks/{id}/delegation-reply` with
|
|
78
|
+
`result_id` and `message`. Exits 0 but warns when the backend answers
|
|
79
|
+
`delivered: false` (result is not an open question) or `uncertain` (check
|
|
80
|
+
`status` before resending).
|
|
81
|
+
- **Output** — in `--json` mode `console.emit_result(delegation=<backend body>)`,
|
|
82
|
+
i.e. one line `{"result": "ok", "delegation": {…}}`; otherwise a short
|
|
83
|
+
human summary.
|
|
84
|
+
|
|
85
|
+
## Payload shape
|
|
86
|
+
|
|
87
|
+
`delegation_metadata` on create:
|
|
88
|
+
|
|
89
|
+
- `id` — `uuid5(NAMESPACE_URL, json [target, key])`, stable per target+key.
|
|
90
|
+
- `requester_key` — the `--id` value.
|
|
91
|
+
- `origin_kind` — always `external`; `origin_agent_id`, `origin_chat_id`,
|
|
92
|
+
`origin_task_id` — always null (a CLI requester is not a platform agent,
|
|
93
|
+
chat or task).
|
|
94
|
+
- `depth` — 1 or 2.
|
|
95
|
+
- `root` — own `id` at depth 1; the `--root` value at depth 2.
|
|
96
|
+
- `group` — `--group` or null.
|
|
97
|
+
|
|
98
|
+
There is no config or registry footprint: nothing is written to
|
|
99
|
+
`.cinna/account.json`, `.cinna/config.json` or `~/.cinna/agents.json`. <!-- nocheck -->
|
|
100
|
+
|
|
101
|
+
## External contracts (cinna-core backend dependencies)
|
|
102
|
+
|
|
103
|
+
Owner side — all through `POST /api/v1/cli/account/api-proxy` with the account
|
|
104
|
+
CLI token (`token_type="cli-account"`); the proxy mirrors the inner status and
|
|
105
|
+
body:
|
|
106
|
+
|
|
107
|
+
- `GET tasks/delegation-capabilities` — returns `{version: 1, …}`; 404/405 means
|
|
108
|
+
unsupported.
|
|
109
|
+
- `POST tasks/` — creates the task. **Must deduplicate on `external_ref`**:
|
|
110
|
+
a second create with the same `external_ref` returns the existing task,
|
|
111
|
+
ignores new title/brief, and does not start execution again. The CLI's retry
|
|
112
|
+
safety relies entirely on this.
|
|
113
|
+
- `GET tasks/{id}/detail` — task state, results, open question + result id.
|
|
114
|
+
- `PUT tasks/{id}/delegation-result` — record a result on a task.
|
|
115
|
+
- `POST tasks/{id}/delegation-reply` — answer a question by `result_id`.
|
|
116
|
+
|
|
117
|
+
Executor side — direct call, agent-token auth:
|
|
118
|
+
|
|
119
|
+
- `POST /api/v1/agent/tasks/current/delegation-result` — headers
|
|
120
|
+
`Authorization: Bearer <AGENT_AUTH_TOKEN>`, `X-Agent-Env-Id: <ENV_ID>`; body is
|
|
121
|
+
the report payload plus `source_session_id`. The backend verifies the session
|
|
122
|
+
belongs to this environment's agent and owner, and resolves the task from the
|
|
123
|
+
session. Any 2xx is success.
|
|
124
|
+
|
|
125
|
+
Related, not in this repo: cinna-core's cloud MCP task server exposes a
|
|
126
|
+
`handover_report` tool for the same executor-side report.
|
|
127
|
+
|
|
128
|
+
## Edge cases & guardrails (preserve these)
|
|
129
|
+
|
|
130
|
+
- **Identity is target+key only** — title, brief, group, depth and `--execute`
|
|
131
|
+
must not enter `external_ref` or the delegation id, or retries would create
|
|
132
|
+
duplicates (`src/cinna/delegation.py:create()`).
|
|
133
|
+
- **No local dedup** — do not add a local "already created" cache; the backend
|
|
134
|
+
dedup is the single source of truth.
|
|
135
|
+
- **Depth/root validated client-side** — depth outside 1–2, `--root` at depth
|
|
136
|
+
1, or missing `--root` at depth 2 are usage errors before any request.
|
|
137
|
+
- **Capability gate first** — every owner-side verb gates before its real call;
|
|
138
|
+
an unsupported server gets no task-route traffic.
|
|
139
|
+
- **Blocked requires a question; artifacts validated** — rejected client-side
|
|
140
|
+
(`report()`), never sent half-valid to the backend.
|
|
141
|
+
- **Executor env is mandatory** — missing `AGENT_AUTH_TOKEN`, `BACKEND_URL` or
|
|
142
|
+
`ENV_ID`, or an unreadable context / missing `backend_session_id`, fails
|
|
143
|
+
before any network call.
|
|
144
|
+
- **No redirects on the executor POST** — a redirect would forward the bearer
|
|
145
|
+
token to another origin; `follow_redirects=False` keeps it on `BACKEND_URL`.
|
|
146
|
+
- **Any 2xx accepted** — non-2xx becomes a `PlatformError` (exit 1 for 4xx,
|
|
147
|
+
12 for 5xx) carrying the backend detail.
|
|
148
|
+
- **`--json` envelope** — always `{"result": "ok", "delegation": …}` via
|
|
149
|
+
`console.emit_result()`; errors use the standard `CinnaExit` JSON line.
|