tavus-cli 0.10.2__tar.gz → 0.10.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.
Files changed (117) hide show
  1. tavus_cli-0.10.3/.release-please-manifest.json +3 -0
  2. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/CHANGELOG.md +7 -0
  3. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/PKG-INFO +1 -1
  4. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/packages/tavus/pyproject.toml +2 -2
  5. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/packages/tavus-mcp/pyproject.toml +2 -2
  6. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/pyproject.toml +1 -1
  7. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/skills/tavus-agentic-pals/references/chat-mode.md +18 -0
  8. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/__init__.py +1 -1
  9. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/build_and_verify.py +2 -0
  10. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/carrier_builder.py +36 -19
  11. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_build_and_verify.py +151 -0
  12. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/uv.lock +1 -1
  13. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/package-lock.json +2 -2
  14. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/package.json +1 -1
  15. tavus_cli-0.10.3/workers/tavus-mcp/src/version.ts +1 -0
  16. tavus_cli-0.10.2/.release-please-manifest.json +0 -3
  17. tavus_cli-0.10.2/workers/tavus-mcp/src/version.ts +0 -1
  18. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.agents/plugins/marketplace.json +0 -0
  19. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.env.example +0 -0
  20. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.github/workflows/ci.yml +0 -0
  21. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.github/workflows/deploy-cloudflare-worker.yml +0 -0
  22. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.github/workflows/pr-reviews.yml +0 -0
  23. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.github/workflows/pypi-cli.yml +0 -0
  24. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.github/workflows/tail-cloudflare-worker.yml +0 -0
  25. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.github/workflows/validate-staging-deployment.yml +0 -0
  26. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/.gitignore +0 -0
  27. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/AGENTS.md +0 -0
  28. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/PROMPT.md +0 -0
  29. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/README.md +0 -0
  30. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/docs/builder-guidance.md +0 -0
  31. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/docs/chatgpt-plugin.md +0 -0
  32. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/docs/gemini-enterprise/README.md +0 -0
  33. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/docs/gemini-enterprise/listing.md +0 -0
  34. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/docs/gemini-enterprise/test-queries.md +0 -0
  35. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/docs/usage-observability.md +0 -0
  36. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/packages/tavus/README.md +0 -0
  37. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/packages/tavus-mcp/README.md +0 -0
  38. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/plugins/tavus-pal/assets/icon-dark.svg +0 -0
  39. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/plugins/tavus-pal/assets/icon.svg +0 -0
  40. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/plugins/tavus-pal/mcp.json +0 -0
  41. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/plugins/tavus-pal/plugin.json +0 -0
  42. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/release-please-config.json +0 -0
  43. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/scripts/__init__.py +0 -0
  44. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/scripts/build_chatgpt_plugin.py +0 -0
  45. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/scripts/check_release_versions.py +0 -0
  46. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/skills/tavus-agentic-pals/SKILL.md +0 -0
  47. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/skills/tavus-agentic-pals/agents/openai.yaml +0 -0
  48. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/skills/tavus-agentic-pals/references/build-and-verify.md +0 -0
  49. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/skills/tavus-agentic-pals/references/builder-workflow.md +0 -0
  50. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/skills/tavus-agentic-pals/references/examples.md +0 -0
  51. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/builder_guidance.json +0 -0
  52. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/builder_guidance.py +0 -0
  53. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/cli/__init__.py +0 -0
  54. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/cli/main.py +0 -0
  55. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/__init__.py +0 -0
  56. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/auth/__init__.py +0 -0
  57. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/auth/keyring_store.py +0 -0
  58. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/auth/oauth.py +0 -0
  59. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/auth/session.py +0 -0
  60. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/client/__init__.py +0 -0
  61. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/client/http.py +0 -0
  62. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/daily_transport.py +0 -0
  63. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/env.py +0 -0
  64. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/errors.py +0 -0
  65. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/pal_urls.py +0 -0
  66. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/patch.py +0 -0
  67. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/__init__.py +0 -0
  68. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/capabilities.py +0 -0
  69. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/deployments.py +0 -0
  70. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/knowledge.py +0 -0
  71. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/options.py +0 -0
  72. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/quickstart.py +0 -0
  73. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/scaffold_embed.py +0 -0
  74. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/templates.py +0 -0
  75. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/recipes/tool_reuse.py +0 -0
  76. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/__init__.py +0 -0
  77. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/deployment.py +0 -0
  78. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/file_manifest.py +0 -0
  79. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/guardrail.py +0 -0
  80. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/objective.py +0 -0
  81. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/pal.py +0 -0
  82. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/pronunciation.py +0 -0
  83. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/sdk/schemas/tool.py +0 -0
  84. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/src/tavus_mcp/server.py +0 -0
  85. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/conftest.py +0 -0
  86. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/integration/test_cli.py +0 -0
  87. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/integration/test_client.py +0 -0
  88. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/integration/test_mcp_server.py +0 -0
  89. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/integration/test_oauth.py +0 -0
  90. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_auth.py +0 -0
  91. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_builder.py +0 -0
  92. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_capabilities.py +0 -0
  93. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_daily_transport.py +0 -0
  94. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_deployments.py +0 -0
  95. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_env.py +0 -0
  96. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_guardrail_schema.py +0 -0
  97. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_objective_schema.py +0 -0
  98. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_pal_schema.py +0 -0
  99. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_patch.py +0 -0
  100. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_pronunciation_schema.py +0 -0
  101. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_release_versions.py +0 -0
  102. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_scaffold_embed.py +0 -0
  103. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_tool_reuse.py +0 -0
  104. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/tests/unit/test_tool_schema.py +0 -0
  105. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/.dev.vars.example +0 -0
  106. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/src/index.ts +0 -0
  107. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/src/oauth-grant.ts +0 -0
  108. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/src/oauth-session.ts +0 -0
  109. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/src/oauth.ts +0 -0
  110. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/src/tavus.ts +0 -0
  111. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/test/builder-guidance.test.ts +0 -0
  112. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/test/oauth-session.test.ts +0 -0
  113. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/test/parity.test.ts +0 -0
  114. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/test/tavus.test.ts +0 -0
  115. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/test/worker-runtime.ts +0 -0
  116. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/tsconfig.json +0 -0
  117. {tavus_cli-0.10.2 → tavus_cli-0.10.3}/workers/tavus-mcp/wrangler.jsonc +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.10.3"
3
+ }
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.3](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.10.2...v0.10.3) (2026-10-16)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * preserve every received Builder reply in MCP history ([#78](https://github.com/Tavus-Engineering/tavus-mcp/issues/78)) ([7dab660](https://github.com/Tavus-Engineering/tavus-mcp/commit/7dab660ddaa3110f208f343e90981217c10cdb20))
9
+
3
10
  ## [0.10.2](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.10.1...v0.10.2) (2026-10-16)
4
11
 
5
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tavus-cli
3
- Version: 0.10.2
3
+ Version: 0.10.3
4
4
  Summary: Tavus CLI and MCP server for agentic PAL creation and editing.
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: fastmcp>=2.0.0
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "tavus"
3
- version = "0.10.2" # x-release-please-version
3
+ version = "0.10.3" # x-release-please-version
4
4
  description = "Official alias for tavus-cli, the Tavus CLI and MCP server."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
7
  dependencies = [
8
- "tavus-cli==0.10.2", # x-release-please-version
8
+ "tavus-cli==0.10.3", # x-release-please-version
9
9
  ]
10
10
 
11
11
  [project.urls]
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "tavus-mcp"
3
- version = "0.10.2" # x-release-please-version
3
+ version = "0.10.3" # x-release-please-version
4
4
  description = "Official alias for tavus-cli, which provides the tavus-mcp MCP server."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
7
  dependencies = [
8
- "tavus-cli==0.10.2", # x-release-please-version
8
+ "tavus-cli==0.10.3", # x-release-please-version
9
9
  ]
10
10
 
11
11
  [project.urls]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tavus-cli"
3
- version = "0.10.2"
3
+ version = "0.10.3"
4
4
  description = "Tavus CLI and MCP server for agentic PAL creation and editing."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -88,6 +88,24 @@ timeout covers the join, but budget the first `collect_turn` generously.
88
88
  Always `close()` the session and end the conversation in `finally:` —
89
89
  orphaned chat sessions still consume quota.
90
90
 
91
+ ## Conversation history
92
+
93
+ Reuse the returned `conversation_id` for each turn of the same test. CVI owns
94
+ that conversation's user and assistant history; starting another conversation
95
+ does not restore the previous test. In chat mode, CVI must commit each reply
96
+ before delivering it, without waiting for audio playback. The MCP client does
97
+ not replay earlier turns into the prompt. If a PAL repeats answered questions,
98
+ check that its delivered replies appear in the next CVI model input; seeing
99
+ them in the tool result alone does not prove they were retained.
100
+
101
+ Builder history is separate. `tavus_pal_builder_history` reads the creator's
102
+ saved dialogue with Charlie, not the PAL's test conversation. The local
103
+ Builder recipe saves each received reply before executing tools or requesting
104
+ a continuation, so a later timeout does not erase earlier dialogue. Automatic
105
+ drafting instructions and test/refinement prompts are not saved as creator
106
+ messages. A resumed Builder receives a bounded snapshot from request-handler;
107
+ the full saved dialogue remains available through the history tools.
108
+
91
109
  For fully offline unit tests, monkeypatch
92
110
  `daily_transport.open_conversation_session` with a scripted fake session (see
93
111
  `tests/unit/test_build_and_verify.py::FakeSession`).
@@ -1,4 +1,4 @@
1
1
  """Tavus CLI and MCP server package."""
2
2
 
3
3
  __all__ = ["__version__"]
4
- __version__ = "0.10.2" # x-release-please-version
4
+ __version__ = "0.10.3" # x-release-please-version
@@ -114,6 +114,8 @@ async def build_and_verify(
114
114
  answer_provider=None,
115
115
  on_builder_reply=None,
116
116
  transcript=build_transcript,
117
+ record_creator_prompt=False,
118
+ creator_request=prompt,
117
119
  )
118
120
  await client.builders.publish(builder_id)
119
121
  spec = await _assemble_spec(client, pal_id)
@@ -95,15 +95,19 @@ async def run_carrier_build(
95
95
  answer_provider: AnswerProvider | None,
96
96
  on_builder_reply: BuildProgress | None,
97
97
  transcript: list[dict[str, Any]],
98
+ record_creator_prompt: bool = True,
99
+ creator_request: str | None = None,
98
100
  ) -> str:
99
101
  """Build or refine one PAL through a real, headless Charlie conversation."""
102
+ initial_request = creator_request if creator_request is not None else prompt
100
103
  runtime: dict[str, Any] = {
101
104
  "pal_id": pal_id,
102
105
  "builder_id": builder_id,
103
106
  "read_documents": {},
104
107
  "drafted": False,
108
+ "creator_request": initial_request,
105
109
  }
106
- initial_context = await build_carrier_context(client, runtime, initial_request=prompt)
110
+ initial_context = await build_carrier_context(client, runtime, initial_request=initial_request)
107
111
  created = await client.conversations.create_headless_builder(
108
112
  {
109
113
  "builder_id": builder_id,
@@ -134,6 +138,7 @@ async def run_carrier_build(
134
138
  human_driven = answer_provider is not None or answers is not None
135
139
  answer_index = 0
136
140
  next_message = _complete_brief(prompt) if not human_driven else prompt
141
+ creator_message = prompt if record_creator_prompt else None
137
142
  max_creator_turns = max_rounds + 1 if human_driven else max_rounds
138
143
 
139
144
  try:
@@ -157,7 +162,8 @@ async def run_carrier_build(
157
162
  client,
158
163
  runtime,
159
164
  next_message,
160
- initial_request=prompt,
165
+ creator_message=creator_message,
166
+ initial_request=initial_request,
161
167
  transcript=transcript,
162
168
  )
163
169
  if on_builder_reply is not None:
@@ -176,6 +182,7 @@ async def run_carrier_build(
176
182
  answer_index += 1
177
183
  else:
178
184
  break
185
+ creator_message = next_message
179
186
  continue
180
187
 
181
188
  if runtime["drafted"]:
@@ -185,6 +192,7 @@ async def run_carrier_build(
185
192
  "Do not ask me another setup question: name the PAL and dispatch "
186
193
  "a complete first draft now."
187
194
  )
195
+ creator_message = None
188
196
 
189
197
  if not runtime["drafted"]:
190
198
  await _run_creator_turn(
@@ -192,7 +200,8 @@ async def run_carrier_build(
192
200
  runtime,
193
201
  "The build must complete now. Infer any remaining choices from the "
194
202
  "original brief and call draft_pal for a complete first draft.",
195
- initial_request=prompt,
203
+ creator_message=None,
204
+ initial_request=initial_request,
196
205
  transcript=transcript,
197
206
  )
198
207
  if not runtime["drafted"]:
@@ -221,27 +230,43 @@ async def _run_creator_turn(
221
230
  runtime: dict[str, Any],
222
231
  message: str,
223
232
  *,
233
+ creator_message: str | None,
224
234
  initial_request: str,
225
235
  transcript: list[dict[str, Any]],
226
236
  ) -> dict[str, Any]:
227
237
  builder_id = str(runtime["builder_id"])
228
238
  session = runtime["session"]
229
- transcript.append({"role": "user", "text": message})
230
- await client.builders.append_messages(
231
- builder_id, [{"role": "user", "content": message}]
232
- )
239
+ if creator_message:
240
+ runtime["creator_request"] = creator_message
241
+ await client.builders.append_messages(
242
+ builder_id, [{"role": "user", "content": creator_message}]
243
+ )
244
+ transcript.append({"role": "user", "text": creator_message})
233
245
 
234
246
  reply = await _send_and_collect(session, message)
235
247
  tool_names: list[str] = []
236
- for _ in range(MAX_TOOL_HOPS):
248
+ reply_texts: list[str] = []
249
+ for hop in range(MAX_TOOL_HOPS + 1):
250
+ text = str(reply.get("text") or "").strip()
251
+ if text:
252
+ # Save before tool work or another inference can fail and lose this reply.
253
+ await client.builders.append_messages(
254
+ builder_id, [{"role": "assistant", "content": text}]
255
+ )
256
+ transcript.append({"role": "assistant", "text": text})
257
+ reply_texts.append(text)
237
258
  calls = list(reply.pop("tool_calls", []))
238
- if not calls:
259
+ if not calls or hop == MAX_TOOL_HOPS:
239
260
  break
240
261
  outcomes: list[str] = []
241
262
  for call in calls:
242
263
  name = str(call.get("name") or "")
243
264
  tool_names.append(name)
244
- outcome = await _execute_carrier_tool(client, runtime, call, message)
265
+ # RQH persists the drafting message too. Keep transport instructions
266
+ # and automated nudges out of the creator's saved dialogue.
267
+ outcome = await _execute_carrier_tool(
268
+ client, runtime, call, str(runtime["creator_request"])
269
+ )
245
270
  outcomes.append(outcome)
246
271
  tool_call_id = str(call.get("tool_call_id") or "")
247
272
  if tool_call_id:
@@ -270,21 +295,13 @@ async def _run_creator_turn(
270
295
  )
271
296
  reply = await _send_and_collect(session, followup)
272
297
 
273
- text = str(reply.get("text") or "").strip()
274
- if not text:
275
- text = _tool_narration(reply, tool_names)
276
298
  structured_reply = {
277
- "text": text,
299
+ "text": "\n\n".join(reply_texts) or _tool_narration(reply, tool_names),
278
300
  "tool_names": tool_names,
279
301
  "draft_ready": bool(runtime["drafted"]),
280
302
  "targets": [],
281
303
  "suggestions": [],
282
304
  }
283
- transcript.append({"role": "assistant", **structured_reply})
284
- if text:
285
- await client.builders.append_messages(
286
- builder_id, [{"role": "assistant", "content": text}]
287
- )
288
305
  return structured_reply
289
306
 
290
307
 
@@ -398,6 +398,10 @@ async def test_build_uses_headless_carrier_and_pal_maker_model(
398
398
  assert len(builder_session.sent_texts) == 2
399
399
  assert routes["fanout"].call_count == 1
400
400
  fanout_body = json.loads(routes["fanout"].calls.last.request.read())
401
+ assert fanout_body["message"] == (
402
+ "Build a warm office greeter for delivery drivers. Route packages to "
403
+ "the lobby shelf and never request access codes."
404
+ )
401
405
  assert fanout_body["briefs"] == {
402
406
  "personality": "Warm office greeter for delivery drivers.",
403
407
  "objectives": "Route each delivery to the lobby shelf.",
@@ -490,6 +494,153 @@ def test_map_draft_briefs_merges_facts_into_personality() -> None:
490
494
  }
491
495
 
492
496
 
497
+ @pytest.mark.asyncio
498
+ @pytest.mark.parametrize("closeout", ["The greeting is updated.", None])
499
+ @respx.mock
500
+ async def test_carrier_preserves_replies_before_a_tool_followup(
501
+ clean_env: Path, monkeypatch: pytest.MonkeyPatch, closeout: str | None
502
+ ) -> None:
503
+ """Regression: tool continuations lost earlier replies, especially on timeout."""
504
+ _write_env(clean_env)
505
+ routes = _register_build_routes()
506
+ saved: list[dict[str, str]] = []
507
+
508
+ def append(request):
509
+ saved.extend(json.loads(request.content)["messages"])
510
+ return Response(200, json=saved)
511
+
512
+ routes["append"].mock(side_effect=append)
513
+ respx.get(f"{PUBLIC}/builder/b1").mock(
514
+ side_effect=lambda _: Response(200, json={"session": {"chat_history": saved}})
515
+ )
516
+ first = "I will keep the no-Memory decision and update the greeting."
517
+ turns = [
518
+ {
519
+ "text": first,
520
+ "tool_calls": [
521
+ {
522
+ "name": "draft_pal",
523
+ "tool_call_id": "greeting",
524
+ "arguments": json.dumps({"greeting_brief": "Ask about deliveries."}),
525
+ }
526
+ ],
527
+ }
528
+ ]
529
+ if closeout:
530
+ turns.append({"text": closeout, "tool_calls": []})
531
+ session = FakeSession("c_builder", turns)
532
+
533
+ async def open_session(*_args: Any, **_kwargs: Any) -> FakeSession:
534
+ return session
535
+
536
+ monkeypatch.setattr(daily_transport, "open_conversation_session", open_session)
537
+ transcript: list[dict[str, Any]] = []
538
+ async with await _make_client() as client:
539
+
540
+ async def run():
541
+ return await carrier_builder.run_carrier_build(
542
+ client,
543
+ builder_id="b1",
544
+ pal_id="p1",
545
+ prompt="Change the greeting.",
546
+ max_rounds=1,
547
+ answers=[],
548
+ answer_provider=None,
549
+ on_builder_reply=None,
550
+ transcript=transcript,
551
+ )
552
+
553
+ if closeout:
554
+ await run()
555
+ else:
556
+ with pytest.raises(TavusError, match="No CVI reply"):
557
+ await run()
558
+ history = await client.builders.chat_history("b1")
559
+
560
+ expected = [
561
+ {"role": "user", "content": "Change the greeting."},
562
+ {"role": "assistant", "content": first},
563
+ ]
564
+ if closeout:
565
+ expected.append({"role": "assistant", "content": closeout})
566
+ assert history["messages"] == expected
567
+ assert [{"role": row["role"], "content": row["text"]} for row in transcript] == expected
568
+ assert session.closed
569
+ assert routes["builder_end"].called
570
+
571
+
572
+ @pytest.mark.asyncio
573
+ @pytest.mark.parametrize("max_rounds", [1, 2])
574
+ @respx.mock
575
+ async def test_carrier_does_not_save_automatic_nudges_as_creator_messages(
576
+ clean_env: Path, monkeypatch: pytest.MonkeyPatch, max_rounds: int
577
+ ) -> None:
578
+ """Regression: resuming a build must not attribute client instructions to its creator."""
579
+ _write_env(clean_env)
580
+ routes = _register_build_routes()
581
+ session = FakeSession(
582
+ "c_builder",
583
+ [
584
+ {"text": "Who will use this PAL?", "tool_calls": []},
585
+ {
586
+ "text": "I will draft the greeter.",
587
+ "tool_calls": [
588
+ {
589
+ "name": "draft_pal",
590
+ "tool_call_id": "draft",
591
+ "arguments": json.dumps({"sections_brief": "An office greeter."}),
592
+ }
593
+ ],
594
+ },
595
+ {"text": "The greeter is ready.", "tool_calls": []},
596
+ ],
597
+ )
598
+
599
+ refinement = FakeSession("c_builder", [
600
+ {"text": "I will refine the greeting.", "tool_calls": [{
601
+ "name": "draft_pal", "tool_call_id": "refine",
602
+ "arguments": json.dumps({"greeting_brief": "Ask about deliveries."}),
603
+ }]},
604
+ {"text": "The greeting is ready.", "tool_calls": []},
605
+ ])
606
+ builders = iter([session, refinement])
607
+
608
+ async def open_session(conversation_id: str, *_args: Any, **_kwargs: Any) -> FakeSession:
609
+ if conversation_id == "c_builder":
610
+ return next(builders)
611
+ return FakeSession("c_smoke", [{"text": "Hello!", "tool_calls": []}])
612
+
613
+ routes["judge"].mock(side_effect=[
614
+ Response(200, json={"overall": "partial", "summary": "Improve the greeting."}),
615
+ Response(200, json={"overall": "pass"}),
616
+ ])
617
+ monkeypatch.setattr(daily_transport, "open_conversation_session", open_session)
618
+ async with await _make_client() as client:
619
+ result = await build_and_verify(
620
+ client, prompt="Build an office greeter.", face_id="rabc",
621
+ max_rounds=max_rounds, probes=["Hello"], auto_refine=True, max_refine_rounds=1,
622
+ )
623
+ transcript = result["build_transcript"]
624
+
625
+ saved = [
626
+ row
627
+ for call in routes["append"].calls
628
+ for row in json.loads(call.request.content)["messages"]
629
+ ]
630
+ assert [row for row in saved if row["role"] == "user"] == [
631
+ {"role": "user", "content": "Build an office greeter."},
632
+ ]
633
+ assert [row for row in transcript if row["role"] == "user"] == [
634
+ {"role": "user", "text": "Build an office greeter."},
635
+ ]
636
+ assert len(session.sent_texts) == 3
637
+ # RQH also saves this field; automated drafting must not add a fake user turn.
638
+ assert all(
639
+ json.loads(call.request.content)["message"] == "Build an office greeter."
640
+ for call in routes["fanout"].calls
641
+ )
642
+
643
+
493
644
  @pytest.mark.parametrize(
494
645
  "args",
495
646
  [
@@ -1303,7 +1303,7 @@ wheels = [
1303
1303
 
1304
1304
  [[package]]
1305
1305
  name = "tavus-cli"
1306
- version = "0.10.2"
1306
+ version = "0.10.3"
1307
1307
  source = { editable = "." }
1308
1308
  dependencies = [
1309
1309
  { name = "fastmcp" },
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@tavus/tavus-mcp-worker",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@tavus/tavus-mcp-worker",
9
- "version": "0.10.2",
9
+ "version": "0.10.3",
10
10
  "dependencies": {
11
11
  "@modelcontextprotocol/sdk": "^1.29.0",
12
12
  "agents": "^0.12.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tavus/tavus-mcp-worker",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -0,0 +1 @@
1
+ export const VERSION = "0.10.3"; // x-release-please-version
@@ -1,3 +0,0 @@
1
- {
2
- ".": "0.10.2"
3
- }
@@ -1 +0,0 @@
1
- export const VERSION = "0.10.2"; // x-release-please-version
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes