healthcloud-cli 3.7.5__tar.gz → 3.7.6__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.
Files changed (92) hide show
  1. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/PKG-INFO +20 -9
  2. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/README.md +19 -8
  3. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/sessions/operations.py +38 -7
  4. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/pyproject.toml +1 -1
  5. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/.gitignore +0 -0
  6. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/hatch_build.py +0 -0
  7. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/__init__.py +0 -0
  8. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/admins/__init__.py +0 -0
  9. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/admins/commands.py +0 -0
  10. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/admins/group.py +0 -0
  11. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/admins/operations.py +0 -0
  12. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/__init__.py +0 -0
  13. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/agents/__init__.py +0 -0
  14. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/agents/commands.py +0 -0
  15. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/agents/group.py +0 -0
  16. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/agents/operations.py +0 -0
  17. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/agents/projections.py +0 -0
  18. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/auth.py +0 -0
  19. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/commands.py +0 -0
  20. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/compatibility.py +0 -0
  21. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/context.py +0 -0
  22. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/data_sources/__init__.py +0 -0
  23. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/data_sources/commands.py +0 -0
  24. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/data_sources/group.py +0 -0
  25. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/data_sources/operations.py +0 -0
  26. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/data_sources/projections.py +0 -0
  27. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/departments/__init__.py +0 -0
  28. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/departments/commands.py +0 -0
  29. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/departments/group.py +0 -0
  30. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/departments/operations.py +0 -0
  31. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/departments/projections.py +0 -0
  32. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/domain_errors.py +0 -0
  33. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/errors.py +0 -0
  34. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/group.py +0 -0
  35. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/guardrails/__init__.py +0 -0
  36. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/guardrails/commands.py +0 -0
  37. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/guardrails/group.py +0 -0
  38. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/guardrails/operations.py +0 -0
  39. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/guardrails/projections.py +0 -0
  40. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/organization/__init__.py +0 -0
  41. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/organization/commands.py +0 -0
  42. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/organization/group.py +0 -0
  43. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/organization/operations.py +0 -0
  44. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/organization/projections.py +0 -0
  45. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/output.py +0 -0
  46. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/policies/__init__.py +0 -0
  47. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/policies/commands.py +0 -0
  48. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/policies/group.py +0 -0
  49. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/policies/operations.py +0 -0
  50. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/policies/projections.py +0 -0
  51. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/pretty.py +0 -0
  52. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/registry.py +0 -0
  53. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/results.py +0 -0
  54. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/session.py +0 -0
  55. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/sessions/__init__.py +0 -0
  56. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/sessions/commands.py +0 -0
  57. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/sessions/group.py +0 -0
  58. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/sessions/projections.py +0 -0
  59. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/skills/__init__.py +0 -0
  60. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/skills/commands.py +0 -0
  61. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/skills/group.py +0 -0
  62. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/skills/operations.py +0 -0
  63. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/skills/projections.py +0 -0
  64. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/target.py +0 -0
  65. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/teams/__init__.py +0 -0
  66. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/teams/commands.py +0 -0
  67. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/teams/group.py +0 -0
  68. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/teams/operations.py +0 -0
  69. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/teams/projections.py +0 -0
  70. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/tools/__init__.py +0 -0
  71. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/tools/commands.py +0 -0
  72. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/tools/group.py +0 -0
  73. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/tools/operations.py +0 -0
  74. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/tools/projections.py +0 -0
  75. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/tuning.py +0 -0
  76. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/ui.py +0 -0
  77. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/ams/windows_files.py +0 -0
  78. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/cli.py +0 -0
  79. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/context.py +0 -0
  80. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/contracts/__init__.py +0 -0
  81. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/contracts/mcp-tools.manifest.json +0 -0
  82. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/main.py +0 -0
  83. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/manifest.py +0 -0
  84. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/output.py +0 -0
  85. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/params.py +0 -0
  86. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/tenants/__init__.py +0 -0
  87. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/tenants/commands.py +0 -0
  88. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/tenants/group.py +0 -0
  89. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/tenants/operations.py +0 -0
  90. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/healthcloud_cli/util.py +0 -0
  91. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/scripts/__init__.py +0 -0
  92. {healthcloud_cli-3.7.5 → healthcloud_cli-3.7.6}/scripts/bundle_manifest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: healthcloud-cli
3
- Version: 3.7.5
3
+ Version: 3.7.6
4
4
  Summary: Command-line interface for the HealthCloud MCP gateway, generated from the shared MCP tool manifest.
5
5
  Project-URL: Issues, https://github.com/healthcloudservices/healthcloud-services/issues
6
6
  Project-URL: Repository, https://github.com/healthcloudservices/healthcloud-services
@@ -1099,8 +1099,12 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
1099
1099
  the envelope carries the server's authoritative readback:
1100
1100
  `operation: "sessions.create"` with `data.session`, and
1101
1101
  `operation: "sessions.messages.add"` with `data.message` (message ID, role,
1102
- mode, plan ID, model, timestamps) plus `data.idempotency_key` when a key was
1103
- supplied. Protected free text — `--initial-prompt` and `--content` — is sent to
1102
+ mode, plan ID, model, timestamps) plus `data.idempotency_key` — the key the
1103
+ server read back, when the returned message carries one. When the readback
1104
+ does not echo a key, the field is omitted: the envelope never asserts a
1105
+ key→message association the server did not confirm. An `--idempotency-key`
1106
+ longer than 128 characters (or an empty one) is refused locally with a
1107
+ `validation` error naming the flag, before anything is sent. Protected free text — `--initial-prompt` and `--content` — is sent to
1104
1108
  the server but never appears in the envelope, a refusal, a diagnostic or a log.
1105
1109
  - The protected free text can also arrive from an owner-only file, which keeps it
1106
1110
  out of the command line entirely (argv, the process table under
@@ -1131,16 +1135,18 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
1131
1135
  - `--mode implement` is not available from the server yet: `create` reports one
1132
1136
  `unsupported` refusal (the server answers 501) and creates nothing. A message
1133
1137
  whose `--mode` differs from its session's mode is refused by the server with
1134
- 409 and reported as one write-worded `conflict` refusal that says a message's
1135
- mode must match its session's mode and points at `hc ams sessions get`; no
1136
- message is added, and the read-era conflict wording is never used.
1138
+ 409 and reported as one write-worded `conflict` refusal that names the
1139
+ session, mentions a mode mismatch as one possible cause, and points at
1140
+ `hc ams sessions get`; no message is added, and the read-era conflict wording
1141
+ is never used.
1137
1142
  - A missing, expired or other user's session is a `not_found` refusal naming the
1138
1143
  session on `get`, `messages list` and `messages add`; on `create`, a 404 names
1139
1144
  the requested `--agent-id`.
1140
1145
  - Messages with an `--idempotency-key` are replay-safe: the server answers 201
1141
1146
  with the existing message instead of inserting a second one, and the command
1142
1147
  reports that message (its original ID) — a replay is never reported as a new
1143
- write, and the command itself never re-sends a write.
1148
+ write, and the command itself never re-sends a write. The envelope echoes the
1149
+ server's read-back key, never a key the server did not confirm.
1144
1150
  - If the transport times out or the connection drops mid-write, the write's
1145
1151
  outcome is unknown: the CLI reports one `transport` error whose message says so
1146
1152
  and points at `hc ams sessions list`/`hc ams sessions messages list` before
@@ -1149,8 +1155,10 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
1149
1155
  landed write is never reported as stopped.
1150
1156
  - Reads refuse target files, root credentials or base URLs, raw output, blank or
1151
1157
  dot-segment session IDs, out-of-range limits, and installed SDKs without the
1152
- session list/get and message list/add methods before tenant-admin login or any
1153
- read. The existing `hc ams plan|show|apply|restore` tuning commands and their
1158
+ session list/get and message list methods before tenant-admin login or any
1159
+ read; `messages list` needs only `messages.list` — the write method `add` is
1160
+ not required for the read, and `messages add` still refuses `unsupported`
1161
+ without it. The existing `hc ams plan|show|apply|restore` tuning commands and their
1154
1162
  signatures are unchanged.
1155
1163
  - These session and message commands do not attach files, apply or roll back
1156
1164
  configuration, or send session history to a model. Plan review is its own
@@ -1241,6 +1249,9 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
1241
1249
  message write, no `PUT` and no apply. The server runs the planner once. It
1242
1250
  requires a saved `hc login` session and reports
1243
1251
  `operation: "sessions.plans.generate"` with `data.plan` and `data.out`.
1252
+ An `--idempotency-key` longer than 128 characters (or an empty one) is
1253
+ refused locally with a `validation` error naming the flag, before anything
1254
+ is sent.
1244
1255
  `get` sends exactly one `GET /vibecoder/plans/PLAN_ID` and reports
1245
1256
  `operation: "sessions.plans.get"`; a reload is immutable, so it returns the
1246
1257
  same `plan_hash` and diff however the session changed afterwards. `get` is a
@@ -1081,8 +1081,12 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
1081
1081
  the envelope carries the server's authoritative readback:
1082
1082
  `operation: "sessions.create"` with `data.session`, and
1083
1083
  `operation: "sessions.messages.add"` with `data.message` (message ID, role,
1084
- mode, plan ID, model, timestamps) plus `data.idempotency_key` when a key was
1085
- supplied. Protected free text — `--initial-prompt` and `--content` — is sent to
1084
+ mode, plan ID, model, timestamps) plus `data.idempotency_key` — the key the
1085
+ server read back, when the returned message carries one. When the readback
1086
+ does not echo a key, the field is omitted: the envelope never asserts a
1087
+ key→message association the server did not confirm. An `--idempotency-key`
1088
+ longer than 128 characters (or an empty one) is refused locally with a
1089
+ `validation` error naming the flag, before anything is sent. Protected free text — `--initial-prompt` and `--content` — is sent to
1086
1090
  the server but never appears in the envelope, a refusal, a diagnostic or a log.
1087
1091
  - The protected free text can also arrive from an owner-only file, which keeps it
1088
1092
  out of the command line entirely (argv, the process table under
@@ -1113,16 +1117,18 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
1113
1117
  - `--mode implement` is not available from the server yet: `create` reports one
1114
1118
  `unsupported` refusal (the server answers 501) and creates nothing. A message
1115
1119
  whose `--mode` differs from its session's mode is refused by the server with
1116
- 409 and reported as one write-worded `conflict` refusal that says a message's
1117
- mode must match its session's mode and points at `hc ams sessions get`; no
1118
- message is added, and the read-era conflict wording is never used.
1120
+ 409 and reported as one write-worded `conflict` refusal that names the
1121
+ session, mentions a mode mismatch as one possible cause, and points at
1122
+ `hc ams sessions get`; no message is added, and the read-era conflict wording
1123
+ is never used.
1119
1124
  - A missing, expired or other user's session is a `not_found` refusal naming the
1120
1125
  session on `get`, `messages list` and `messages add`; on `create`, a 404 names
1121
1126
  the requested `--agent-id`.
1122
1127
  - Messages with an `--idempotency-key` are replay-safe: the server answers 201
1123
1128
  with the existing message instead of inserting a second one, and the command
1124
1129
  reports that message (its original ID) — a replay is never reported as a new
1125
- write, and the command itself never re-sends a write.
1130
+ write, and the command itself never re-sends a write. The envelope echoes the
1131
+ server's read-back key, never a key the server did not confirm.
1126
1132
  - If the transport times out or the connection drops mid-write, the write's
1127
1133
  outcome is unknown: the CLI reports one `transport` error whose message says so
1128
1134
  and points at `hc ams sessions list`/`hc ams sessions messages list` before
@@ -1131,8 +1137,10 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
1131
1137
  landed write is never reported as stopped.
1132
1138
  - Reads refuse target files, root credentials or base URLs, raw output, blank or
1133
1139
  dot-segment session IDs, out-of-range limits, and installed SDKs without the
1134
- session list/get and message list/add methods before tenant-admin login or any
1135
- read. The existing `hc ams plan|show|apply|restore` tuning commands and their
1140
+ session list/get and message list methods before tenant-admin login or any
1141
+ read; `messages list` needs only `messages.list` — the write method `add` is
1142
+ not required for the read, and `messages add` still refuses `unsupported`
1143
+ without it. The existing `hc ams plan|show|apply|restore` tuning commands and their
1136
1144
  signatures are unchanged.
1137
1145
  - These session and message commands do not attach files, apply or roll back
1138
1146
  configuration, or send session history to a model. Plan review is its own
@@ -1223,6 +1231,9 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
1223
1231
  message write, no `PUT` and no apply. The server runs the planner once. It
1224
1232
  requires a saved `hc login` session and reports
1225
1233
  `operation: "sessions.plans.generate"` with `data.plan` and `data.out`.
1234
+ An `--idempotency-key` longer than 128 characters (or an empty one) is
1235
+ refused locally with a `validation` error naming the flag, before anything
1236
+ is sent.
1226
1237
  `get` sends exactly one `GET /vibecoder/plans/PLAN_ID` and reports
1227
1238
  `operation: "sessions.plans.get"`; a reload is immutable, so it returns the
1228
1239
  same `plan_hash` and diff however the session changed afterwards. `get` is a
@@ -87,6 +87,28 @@ def _session_id(value: str) -> str:
87
87
  return value
88
88
 
89
89
 
90
+ IDEMPOTENCY_KEY_MAX_CHARS = 128
91
+
92
+
93
+ def _idempotency_key(value, refuse):
94
+ """Mirror the SDK's 128-character bound locally (the help text advertises it).
95
+
96
+ Refused before login or any request, so the bound holds regardless of the
97
+ installed SDK version. A non-empty string within the bound passes through
98
+ unchanged; the caller keeps its own refusal class (message write vs plan
99
+ review).
100
+ """
101
+ if value is None:
102
+ return None
103
+ if not isinstance(value, str) or not value or len(value) > IDEMPOTENCY_KEY_MAX_CHARS:
104
+ raise refuse(
105
+ "validation",
106
+ f"--idempotency-key must be a non-empty string of at most {IDEMPOTENCY_KEY_MAX_CHARS} "
107
+ "characters; nothing was sent.",
108
+ )
109
+ return value
110
+
111
+
90
112
  def _limit(value: int | None, maximum: int, name: str) -> int | None:
91
113
  if value is None:
92
114
  return None
@@ -572,7 +594,9 @@ def get_session(dependencies: AmsDependencies, target: TenantTarget, session_id:
572
594
  with dependencies.authenticated_sdk_factory(target) as sdk:
573
595
  session = _sessions(sdk).get(session_id)
574
596
  if session is None:
575
- raise SessionReadRefused("validation", "Session/tenant identity mismatch; stopped.")
597
+ # A 200 with a JSON null body is the server saying "no such session
598
+ # here" — not an identity mismatch.
599
+ raise SessionReadRefused("not_found", f"Session '{session_id}' was not found in this tenant.")
576
600
  _identity(session, target, session_id)
577
601
  return succeeded(
578
602
  "sessions.get", _target(target, session_id), {"session": session_projection(session)}, read_only=True
@@ -622,9 +646,9 @@ def _message_conflict(error: BaseException, session_id: str) -> SessionWriteRefu
622
646
  return None
623
647
  return SessionWriteRefused(
624
648
  "conflict",
625
- f"The message was refused for session '{session_id}': the server reported a conflict. A "
626
- "message's mode must match its session's mode; read the session with `hc ams sessions get` "
627
- "before retrying. No message was added.",
649
+ f"The message was refused for session '{session_id}': the server reported a conflict — for "
650
+ "example, a message's mode not matching its session's mode. Read the session with "
651
+ "`hc ams sessions get` before retrying. No message was added.",
628
652
  )
629
653
 
630
654
 
@@ -709,7 +733,7 @@ def list_messages(
709
733
  limit = _limit(limit, MESSAGE_LIMIT_MAX, "--limit")
710
734
  with dependencies.authenticated_sdk_factory(target) as sdk:
711
735
  try:
712
- messages = _messages(sdk).list(session_id, **({} if limit is None else {"limit": limit}))
736
+ messages = _messages(sdk, ("list",)).list(session_id, **({} if limit is None else {"limit": limit}))
713
737
  except HealthCloudHTTPError as error:
714
738
  if getattr(error, "status_code", None) == 404:
715
739
  raise SessionReadRefused(
@@ -944,6 +968,7 @@ def generate_plan(
944
968
  session_id = _session_id(session_id)
945
969
  operation = _plan_operation(operation)
946
970
  prompt = None if prompt_file is None else _prompt_file(prompt_file)
971
+ idempotency_key = _idempotency_key(idempotency_key, PlanReviewRefused)
947
972
  with private_output(out) as write, dependencies.authenticated_sdk_factory(target) as sdk:
948
973
  resource = _plans(sdk, ("generate",), write=True)
949
974
  try:
@@ -1038,6 +1063,7 @@ def add_message(
1038
1063
  refuse=SessionWriteRefused,
1039
1064
  required=True,
1040
1065
  )
1066
+ idempotency_key = _idempotency_key(idempotency_key, SessionWriteRefused)
1041
1067
  with dependencies.authenticated_sdk_factory(target) as sdk:
1042
1068
  resource = _messages(sdk, ("add",), write=True)
1043
1069
  try:
@@ -1067,8 +1093,13 @@ def add_message(
1067
1093
  "anything else.",
1068
1094
  )
1069
1095
  data: dict = {"message": message_projection(message)}
1070
- if idempotency_key is not None:
1071
- data["idempotency_key"] = idempotency_key
1096
+ # Echo the server's readback key when the message carries one, so the
1097
+ # envelope never asserts a key→message association the server did not
1098
+ # confirm. When the readback omits the key entirely the field is omitted
1099
+ # too — the caller can see the key they passed in their own command line.
1100
+ readback_key = getattr(message, "idempotency_key", None)
1101
+ if readback_key is not None:
1102
+ data["idempotency_key"] = readback_key
1072
1103
  return succeeded("sessions.messages.add", _target(target, session_id), data, read_only=False)
1073
1104
 
1074
1105
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "healthcloud-cli"
7
- version = "3.7.5"
7
+ version = "3.7.6"
8
8
  description = "Command-line interface for the HealthCloud MCP gateway, generated from the shared MCP tool manifest."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14"