@oneciel-ai/ciel-runtime 0.2.37 → 0.2.38
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.
- package/CHANGELOG.md +78 -0
- package/README.md +5 -2
- package/ciel_runtime.py +94 -96
- package/ciel_runtime_support/anthropic_model_policy.py +20 -3
- package/ciel_runtime_support/architecture.py +5 -0
- package/ciel_runtime_support/channel_inflight.py +4 -4
- package/ciel_runtime_support/channel_injection.py +56 -1
- package/ciel_runtime_support/channel_mcp_tools.py +34 -0
- package/ciel_runtime_support/channel_message_policy.py +9 -1
- package/ciel_runtime_support/channel_message_prompt.py +23 -0
- package/ciel_runtime_support/channel_pending_injection.py +86 -32
- package/ciel_runtime_support/channel_pending_poll.py +6 -2
- package/ciel_runtime_support/channel_runtime_environment.py +0 -11
- package/ciel_runtime_support/channel_terminal_context.py +0 -6
- package/ciel_runtime_support/channel_terminal_proxy.py +3 -42
- package/ciel_runtime_support/channel_transcript_repository.py +16 -1
- package/ciel_runtime_support/channel_wake_claim_repository.py +0 -19
- package/ciel_runtime_support/channel_wake_context.py +6 -13
- package/ciel_runtime_support/channel_wake_delivery_repository.py +50 -0
- package/ciel_runtime_support/chat_http_controller.py +88 -5
- package/ciel_runtime_support/claude_environment.py +100 -3
- package/ciel_runtime_support/claude_launch_assembly.py +4 -0
- package/ciel_runtime_support/claude_session_socket.py +181 -0
- package/ciel_runtime_support/cli_application_context.py +18 -10
- package/ciel_runtime_support/cli_dispatch.py +19 -3
- package/ciel_runtime_support/cli_parser.py +2 -0
- package/ciel_runtime_support/cli_usage.py +3 -2
- package/ciel_runtime_support/codex_backend_context.py +8 -3
- package/ciel_runtime_support/codex_completion_gate.py +198 -0
- package/ciel_runtime_support/codex_turn_recovery.py +240 -83
- package/ciel_runtime_support/compatibility_protocol.py +5 -2
- package/ciel_runtime_support/config_migrations.py +79 -0
- package/ciel_runtime_support/config_repository.py +7 -0
- package/ciel_runtime_support/context_summary_policy.py +24 -4
- package/ciel_runtime_support/external_event_menu.py +107 -0
- package/ciel_runtime_support/external_event_receiver.py +19 -1
- package/ciel_runtime_support/launch_state.py +2 -0
- package/ciel_runtime_support/managed_tool_injection.py +33 -0
- package/ciel_runtime_support/muse_runtime_context.py +291 -0
- package/ciel_runtime_support/ollama_thinking.py +12 -26
- package/ciel_runtime_support/prelaunch.py +13 -0
- package/ciel_runtime_support/prelaunch_launch_panel.py +2 -1
- package/ciel_runtime_support/prompt_compaction.py +39 -0
- package/ciel_runtime_support/provider_files_proxy.py +248 -0
- package/ciel_runtime_support/provider_model_identity.py +10 -2
- package/ciel_runtime_support/provider_option_cli.py +1 -1
- package/ciel_runtime_support/provider_request_access.py +54 -1
- package/ciel_runtime_support/provider_responses_passthrough.py +152 -15
- package/ciel_runtime_support/providers/alibaba.py +34 -1
- package/ciel_runtime_support/providers/anthropic.py +18 -1
- package/ciel_runtime_support/providers/meta.py +252 -21
- package/ciel_runtime_support/pseudo_tool_parser.py +17 -1
- package/ciel_runtime_support/remote_bridge.py +4 -0
- package/ciel_runtime_support/remote_instructions.py +5 -0
- package/ciel_runtime_support/remote_memory.py +1 -1
- package/ciel_runtime_support/responses_cache_diagnostics.py +82 -0
- package/ciel_runtime_support/responses_custom_tool_bridge.py +282 -0
- package/ciel_runtime_support/responses_input_compatibility.py +19 -3
- package/ciel_runtime_support/router_http.py +301 -4
- package/ciel_runtime_support/router_observability_context.py +26 -0
- package/ciel_runtime_support/router_server_context.py +1 -0
- package/ciel_runtime_support/runtime_adapters.py +31 -0
- package/ciel_runtime_support/runtime_constants.py +23 -1
- package/ciel_runtime_support/runtime_input_gateway.py +85 -5
- package/ciel_runtime_support/runtime_input_status.py +150 -0
- package/ciel_runtime_support/runtime_launch.py +27 -4
- package/ciel_runtime_support/runtime_paths.py +1 -0
- package/ciel_runtime_support/speech_http_controller.py +3 -2
- package/ciel_runtime_support/streaming_anthropic.py +3 -0
- package/ciel_runtime_support/tool_call_events.py +98 -0
- package/ciel_runtime_support/transcript_delta_delivery.py +140 -1
- package/ciel_runtime_support/ui_text.py +1 -0
- package/ciel_runtime_support/web_search_result_events.py +130 -0
- package/ciel_runtime_support/web_ui.py +1 -0
- package/ciel_runtime_support/windows_conpty.py +92 -8
- package/ciel_runtime_support/workspace_mcp.py +4 -1
- package/ciel_runtime_support/workspace_state.py +1 -0
- package/docs/Configuration.md +20 -4
- package/docs/MCP-Channels.md +79 -6
- package/docs/Managed-Tool-Injection.md +25 -0
- package/docs/Module-Map.md +4 -0
- package/docs/Muse-Code.md +73 -0
- package/docs/Observability.md +39 -0
- package/docs/Providers.md +29 -2
- package/docs/Remote-Bridge.md +12 -0
- package/docs/Router.md +1 -0
- package/docs/journal/2026/09/01/diagnostics/anthropic/router/fable51-usage-credits.okf +70 -0
- package/docs/journal/2026/09/01/implementation/claude/session-socket/all-input-paths/default-delivery.okf +133 -0
- package/docs/journal/2026/09/01/implementation/claude/session-socket/windows-direct-input.okf +112 -0
- package/docs/journal/2026/09/02/diagnostics/cache/alibaba/qwen38-post-restart-hit-rate.okf +107 -0
- package/docs/journal/2026/09/02/diagnostics/cache/ollama-cloud/kimi-k3-codex-hit-rate.okf +91 -0
- package/docs/journal/2026/09/02/diagnostics/codex/tui/statusline-cache-metrics.okf +44 -0
- package/docs/journal/2026/09/02/diagnostics/input/transport/fallback-behavior.okf +31 -0
- package/docs/journal/2026/09/02/diagnostics/providers/ollama-cloud/kimi-k3-agent-turn-recovery.okf +125 -0
- package/docs/journal/2026/09/02/diagnostics/providers/ollama-cloud/kimi-k3-parameters.okf +128 -0
- package/docs/journal/2026/09/02/diagnostics/remote/mia/socket-tui-visibility.okf +47 -0
- package/docs/journal/2026/09/02/diagnostics/runtime/alibaba/token-plan/qwen38-latency.okf +108 -0
- package/docs/journal/2026/09/02/diagnostics/runtime/alibaba/token-plan/qwen38-vs-gpt56-latency.okf +92 -0
- package/docs/journal/2026/09/02/implementation/anthropic/context/one-million-defaults.okf +85 -0
- package/docs/journal/2026/09/02/implementation/cache/alibaba/qwen38-cache-hit-improvement.okf +104 -0
- package/docs/journal/2026/09/02/implementation/cache/alibaba/qwen38-cache-hit-improvement.png +0 -0
- package/docs/journal/2026/09/02/implementation/cache/alibaba/responses-session-cache.okf +97 -0
- package/docs/journal/2026/09/02/implementation/providers/alibaba/qwen38/0902-parameter-alignment.okf +97 -0
- package/docs/journal/2026/09/02/implementation/providers/meta/multimodal-tools/contributor-protocol-adoption.okf +140 -0
- package/docs/journal/2026/09/02/implementation/providers/meta/muse-spark-1.3-support.okf +53 -0
- package/docs/journal/2026/09/02/monitoring/cache/alibaba/cielarvis-qwen38-live-hit-rate.okf +858 -0
- package/docs/journal/2026/09/02/operations/release/main-merge-local-deploy.okf +36 -0
- package/docs/journal/2026/09/02/operations/release/nightly/local-cache-deployment.okf +54 -0
- package/docs/journal/2026/09/02/operations/release/nightly/ollama-kimi-recovery-model-audit-deployment.okf +58 -0
- package/docs/journal/2026/09/02/operations/release/nightly/qwen38-cache-improvement-deployment.okf +72 -0
- package/docs/journal/2026/09/02/research/cache/alibaba/qwen38-hit-rate-improvement.okf +233 -0
- package/docs/journal/2026/09/02/research/cache/codex/provider-scope.okf +77 -0
- package/docs/journal/2026/09/02/research/providers/ollama-cloud/desktop-model-exhaustive-audit.okf +159 -0
- package/docs/journal/2026/09/03/diagnostics/providers/meta/muse-spark-contributor-cache-hit.okf +148 -0
- package/docs/journal/2026/09/03/diagnostics/providers/meta/muse-spark-contributor-required-schema.okf +127 -0
- package/docs/journal/2026/09/03/diagnostics/providers/ollama-cloud/kimi-k3-resumed-session-stall.okf +160 -0
- package/docs/journal/2026/09/03/diagnostics/providers/ollama-cloud/kimi-k3-substantive-dangling-action.okf +94 -0
- package/docs/journal/2026/09/03/diagnostics/remote/kevin/codex-gpt-early-turn-completion.okf +162 -0
- package/docs/journal/2026/09/03/implementation/runtimes/meta/muse-code/default-yolo.okf +40 -0
- package/docs/journal/2026/09/03/implementation/runtimes/meta/muse-code/native-subscription-router.okf +86 -0
- package/docs/journal/2026/09/03/operations/deployment/local/session-socket-default-fallback.okf +41 -0
- package/docs/journal/2026/09/03/research/deployment/colab/tailscale/credential-storage.okf +41 -0
- package/docs/journal/2026/09/03/research/providers/meta/muse-code/subscription-billing-boundary.okf +64 -0
- package/docs/journal/2026/09/03/research/web-chat/voice/instruction-format.okf +135 -0
- package/docs/journal/2026/09/04/diagnostics/claude/context/early-auto-compaction.okf +75 -0
- package/docs/journal/2026/09/04/diagnostics/claude/context/fable-51-status-200k.okf +54 -0
- package/docs/journal/2026/09/04/diagnostics/runtime/codex/responses/replayed-item-id-validation.okf +74 -0
- package/docs/journal/2026/09/04/diagnostics/workspaces/onecieldmsui/codex/replay-stall.okf +100 -0
- package/docs/journal/2026/09/04/implementation/observability/tool-calls/websocket-stream.okf +49 -0
- package/docs/journal/2026/09/04/implementation/web-chat/input/raw-injection.okf +42 -0
- package/docs/journal/2026/09/05/diagnostics/tty/claude-raw-idle-render.okf +25 -0
- package/docs/journal/2026/09/05/implementation/events/search-response-urls.okf +23 -0
- package/docs/journal/2026/09/05/implementation/tools/native-injection.okf +19 -0
- package/docs/journal/2026/09/05/implementation/windows/conpty/prompt-delivery-lifecycle.okf +43 -0
- package/docs/journal/2026/09/05/release/nightly/conpty-native-tools.okf +13 -0
- package/docs/journal/2026/09/05/release/nightly/search-result-urls.okf +14 -0
- package/docs/journal/2026/09/06/implementation/codex/inherited-web-mcp.okf +22 -0
- package/docs/journal/2026/09/07/integration/main/nightly-merge.okf +13 -0
- package/docs/journal/2026/09/07/release/stable/0.2.38.okf +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Prelaunch menu projection for the external event receiver."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .external_event_receiver import ExternalEventReceiverService
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def panel_rows(
|
|
11
|
+
service: ExternalEventReceiverService, router_base: str
|
|
12
|
+
) -> tuple[list[str], list[str]]:
|
|
13
|
+
receiver = service.receiver_configs().get("default", {})
|
|
14
|
+
public_receiver = service.public_receiver("default", receiver)
|
|
15
|
+
environment_references = public_receiver.get("environment_references", {})
|
|
16
|
+
enabled = bool(receiver.get("enabled", False))
|
|
17
|
+
transport = str(receiver.get("transport") or "webhook")
|
|
18
|
+
input_transport = str(receiver.get("input_transport") or "auto")
|
|
19
|
+
event_types = (
|
|
20
|
+
receiver.get("event_types")
|
|
21
|
+
if isinstance(receiver.get("event_types"), list)
|
|
22
|
+
else []
|
|
23
|
+
)
|
|
24
|
+
secret_status = service.vault.status("default")
|
|
25
|
+
|
|
26
|
+
def secret_source(field_name: str, stored_key: str) -> str:
|
|
27
|
+
reference = environment_references.get(field_name, {})
|
|
28
|
+
if isinstance(reference, dict) and reference.get("name"):
|
|
29
|
+
availability = "available" if reference.get("available") else "missing"
|
|
30
|
+
return f"env:{reference['name']} {availability}"
|
|
31
|
+
return "stored" if secret_status[stored_key] else "unset"
|
|
32
|
+
|
|
33
|
+
rows = [
|
|
34
|
+
f"Enabled [{'on' if enabled else 'off'}]",
|
|
35
|
+
f"Transport [{transport}]",
|
|
36
|
+
f"Runtime input transport [{input_transport}]",
|
|
37
|
+
f"SSE URL [{str(receiver.get('url') or 'unset')}]",
|
|
38
|
+
"SSE content mode [CloudEvents 1.0 structured JSON]",
|
|
39
|
+
f"Allowed CloudEvent types [{', '.join(str(value) for value in event_types) if event_types else 'all'}]",
|
|
40
|
+
f"Cursor JSON pointer [{str(receiver.get('cursor_json_pointer') or 'SSE id field')}]",
|
|
41
|
+
f"Reconnect query parameter [{str(receiver.get('cursor_query_parameter') or 'Last-Event-ID header')}]",
|
|
42
|
+
f"Webhook signing secret [{secret_source('webhook_secret', 'stored_webhook_secret')}]",
|
|
43
|
+
f"SSE authorization [{secret_source('authorization', 'stored_authorization')}]",
|
|
44
|
+
f"Webhook endpoint [{router_base}/ca/events/webhooks/default]",
|
|
45
|
+
"Back",
|
|
46
|
+
]
|
|
47
|
+
values = [
|
|
48
|
+
"enabled",
|
|
49
|
+
"transport",
|
|
50
|
+
"input_transport",
|
|
51
|
+
"url",
|
|
52
|
+
"__info__",
|
|
53
|
+
"event_types",
|
|
54
|
+
"cursor_json_pointer",
|
|
55
|
+
"cursor_query_parameter",
|
|
56
|
+
"webhook_secret",
|
|
57
|
+
"authorization",
|
|
58
|
+
"__info__",
|
|
59
|
+
"back",
|
|
60
|
+
]
|
|
61
|
+
return rows, values
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def update_config(
|
|
65
|
+
service: ExternalEventReceiverService, key: str, value: Any
|
|
66
|
+
) -> list[str]:
|
|
67
|
+
current = service.receiver_configs().get("default", {})
|
|
68
|
+
body: dict[str, Any] = {
|
|
69
|
+
"enabled": bool(current.get("enabled", False)),
|
|
70
|
+
"transport": str(current.get("transport") or "webhook"),
|
|
71
|
+
"input_transport": str(current.get("input_transport") or "auto"),
|
|
72
|
+
"url": str(current.get("url") or ""),
|
|
73
|
+
"event_types": current.get("event_types")
|
|
74
|
+
if isinstance(current.get("event_types"), list)
|
|
75
|
+
else [],
|
|
76
|
+
"cursor_json_pointer": str(current.get("cursor_json_pointer") or ""),
|
|
77
|
+
"cursor_query_parameter": str(current.get("cursor_query_parameter") or ""),
|
|
78
|
+
}
|
|
79
|
+
if key == "enabled":
|
|
80
|
+
body["enabled"] = not body["enabled"]
|
|
81
|
+
elif key == "transport":
|
|
82
|
+
body["transport"] = "sse" if body["transport"] == "webhook" else "webhook"
|
|
83
|
+
elif key == "input_transport":
|
|
84
|
+
body["input_transport"] = str(value or "auto")
|
|
85
|
+
elif key == "event_types":
|
|
86
|
+
body["event_types"] = [
|
|
87
|
+
part.strip() for part in str(value or "").split(",") if part.strip()
|
|
88
|
+
]
|
|
89
|
+
elif key in {
|
|
90
|
+
"url",
|
|
91
|
+
"cursor_json_pointer",
|
|
92
|
+
"cursor_query_parameter",
|
|
93
|
+
"webhook_secret",
|
|
94
|
+
"authorization",
|
|
95
|
+
}:
|
|
96
|
+
body[key] = str(value or "")
|
|
97
|
+
else:
|
|
98
|
+
raise ValueError(f"unsupported external event option: {key}")
|
|
99
|
+
updated = service.save_receiver("default", body)
|
|
100
|
+
return [
|
|
101
|
+
f"External event receiver updated: enabled={updated.get('enabled')} transport={updated.get('transport')} input_transport={updated.get('input_transport')}.",
|
|
102
|
+
"The router owns receiver connections; the prelaunch configuration process does not open a duplicate stream.",
|
|
103
|
+
"External events use the private Runtime Input Gateway and are never published to Web Chat.",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
__all__ = ["panel_rows", "update_config"]
|
|
@@ -418,6 +418,18 @@ class ExternalEventReceiverService:
|
|
|
418
418
|
cursor_query_parameter = str(body.get("cursor_query_parameter") or "").strip()
|
|
419
419
|
if len(cursor_query_parameter) > 80 or any(char in cursor_query_parameter for char in "&=?#/"):
|
|
420
420
|
raise ValueError("cursor_query_parameter must be a single URL query parameter name")
|
|
421
|
+
input_transport = str(body.get("input_transport") or "auto").strip().lower().replace("-", "_")
|
|
422
|
+
input_transport = {
|
|
423
|
+
"socket": "session_socket",
|
|
424
|
+
"claude_socket": "session_socket",
|
|
425
|
+
"messaging_socket": "session_socket",
|
|
426
|
+
"terminal": "tty",
|
|
427
|
+
"stdin": "tty",
|
|
428
|
+
"llm": "router",
|
|
429
|
+
"context": "router",
|
|
430
|
+
}.get(input_transport, input_transport)
|
|
431
|
+
if input_transport not in {"auto", "session_socket", "tty", "router"}:
|
|
432
|
+
raise ValueError("input_transport must be auto, session_socket, tty, or router")
|
|
421
433
|
cfg = self.load_config()
|
|
422
434
|
root = cfg.setdefault("external_event_receivers", {})
|
|
423
435
|
workspace = root.get(self.workspace_key)
|
|
@@ -433,6 +445,7 @@ class ExternalEventReceiverService:
|
|
|
433
445
|
"event_types": [str(value) for value in event_types if str(value)],
|
|
434
446
|
"cursor_json_pointer": cursor_json_pointer,
|
|
435
447
|
"cursor_query_parameter": cursor_query_parameter,
|
|
448
|
+
"input_transport": input_transport,
|
|
436
449
|
}
|
|
437
450
|
)
|
|
438
451
|
workspace[receiver_id] = current
|
|
@@ -525,7 +538,8 @@ class ExternalEventReceiverService:
|
|
|
525
538
|
return True
|
|
526
539
|
|
|
527
540
|
def _admit(self, receiver_id: str, transport: str, raw_text: str, event: dict[str, str], transport_event_id: str = "") -> dict[str, Any]:
|
|
528
|
-
|
|
541
|
+
receiver = self.receiver_configs().get(receiver_id, {})
|
|
542
|
+
allowed = receiver.get("event_types", [])
|
|
529
543
|
if allowed and event["type"] not in allowed:
|
|
530
544
|
raise ValueError("CloudEvent type is not admitted by this receiver")
|
|
531
545
|
result = self.submit_event(
|
|
@@ -536,6 +550,10 @@ class ExternalEventReceiverService:
|
|
|
536
550
|
event_type=event["type"],
|
|
537
551
|
event_source=event["source"],
|
|
538
552
|
transport_event_id=transport_event_id,
|
|
553
|
+
input_transport=(
|
|
554
|
+
"" if str(receiver.get("input_transport") or "auto") == "auto"
|
|
555
|
+
else str(receiver.get("input_transport") or "")
|
|
556
|
+
),
|
|
539
557
|
)
|
|
540
558
|
self._set_status(receiver_id, state="connected", last_event_id=event["id"], last_event_at=time.time(), error="")
|
|
541
559
|
return result
|
|
@@ -98,6 +98,8 @@ def last_launch_runtime(repository: LaunchStateRepository, cwd_key: str) -> str:
|
|
|
98
98
|
return "agy"
|
|
99
99
|
if mode.startswith("grok"):
|
|
100
100
|
return "grok"
|
|
101
|
+
if mode.startswith("muse"):
|
|
102
|
+
return "muse"
|
|
101
103
|
if mode.startswith("anthropic") or mode.startswith("router:"):
|
|
102
104
|
return "claude"
|
|
103
105
|
return ""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Launch-mode policy for Ciel-owned tools, never user MCP definitions."""
|
|
2
|
+
from collections.abc import Mapping
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def should_inject_tool(*, native: bool, mode: str = "always") -> bool:
|
|
7
|
+
if mode not in {"always", "native", "non_native"}:
|
|
8
|
+
raise ValueError(f"Invalid managed tool injection mode: {mode}")
|
|
9
|
+
return mode == "always" or (mode == "native") == native
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def codex_native_web_tool_overrides(*, native: bool) -> list[str]:
|
|
13
|
+
"""Disable inherited replacement web MCPs for this launch, not on disk."""
|
|
14
|
+
if not native:
|
|
15
|
+
return []
|
|
16
|
+
return [
|
|
17
|
+
"-c", "mcp_servers.duckduckgo.enabled=false",
|
|
18
|
+
"-c", "mcp_servers.web_fetch.enabled=false",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def select_managed_tools(
|
|
23
|
+
servers: Mapping[str, Any], *, native: bool,
|
|
24
|
+
) -> dict[str, Any]:
|
|
25
|
+
selected = {}
|
|
26
|
+
for name, definition in servers.items():
|
|
27
|
+
if not isinstance(definition, dict):
|
|
28
|
+
continue
|
|
29
|
+
item = dict(definition)
|
|
30
|
+
mode = item.pop("injection_mode", "always")
|
|
31
|
+
if should_inject_tool(native=native, mode=mode):
|
|
32
|
+
selected[name] = item
|
|
33
|
+
return selected
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Native Muse Code CLI launch with subscription-safe authentication."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Callable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
MUSE_INSTALL_URL = "https://dev.meta.ai/install.sh"
|
|
11
|
+
MUSE_SUBSCRIPTION_ENV_KEYS = ("META_API_KEY", "MODEL_API_KEY")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def has_option(argv: list[str], *names: str) -> bool:
|
|
15
|
+
return any(
|
|
16
|
+
value in names or any(value.startswith(f"{name}=") for name in names)
|
|
17
|
+
for value in argv
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def option_value(argv: list[str], name: str) -> str:
|
|
22
|
+
for index, value in enumerate(argv):
|
|
23
|
+
if value.startswith(f"{name}="):
|
|
24
|
+
return value.split("=", 1)[1].strip()
|
|
25
|
+
if value == name and index + 1 < len(argv):
|
|
26
|
+
return str(argv[index + 1]).strip()
|
|
27
|
+
return ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class MuseExecutable:
|
|
32
|
+
command: str
|
|
33
|
+
prefix_args: tuple[str, ...] = ()
|
|
34
|
+
platform: str = "native"
|
|
35
|
+
muse_path: str = ""
|
|
36
|
+
transcript_root: Path | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, slots=True)
|
|
40
|
+
class MuseProcessPorts:
|
|
41
|
+
find_executable: Callable[[str], str | None]
|
|
42
|
+
run: Callable[..., Any]
|
|
43
|
+
call: Callable[..., int]
|
|
44
|
+
print_line: Callable[..., None]
|
|
45
|
+
environment: dict[str, str]
|
|
46
|
+
augment_path: Callable[[dict[str, str]], str]
|
|
47
|
+
platform_name: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True)
|
|
51
|
+
class MuseConfigurationPorts:
|
|
52
|
+
load: Callable[[], dict[str, Any]]
|
|
53
|
+
current_provider: Callable[[dict[str, Any]], tuple[str, dict[str, Any]]]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True, slots=True)
|
|
57
|
+
class MuseLifecyclePorts:
|
|
58
|
+
materialize_command: Callable[..., tuple[list[str], dict[str, str]]]
|
|
59
|
+
start_router: Callable[[], Any]
|
|
60
|
+
run_with_router: Callable[[Callable[[], int], bool], int]
|
|
61
|
+
call_with_channel_proxy: Callable[..., int]
|
|
62
|
+
channel_delivery_mode: Callable[[dict[str, Any]], str]
|
|
63
|
+
web_backend_requested: Callable[[dict[str, Any]], bool]
|
|
64
|
+
record_launch: Callable[[str, str], None]
|
|
65
|
+
set_transcript_scope: Callable[..., None]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True, slots=True)
|
|
69
|
+
class MuseRuntimeContext:
|
|
70
|
+
process: MuseProcessPorts
|
|
71
|
+
config: MuseConfigurationPorts
|
|
72
|
+
lifecycle: MuseLifecyclePorts
|
|
73
|
+
|
|
74
|
+
def _native_executable(self) -> MuseExecutable | None:
|
|
75
|
+
executable = self.process.find_executable("muse")
|
|
76
|
+
if executable:
|
|
77
|
+
path = Path(str(executable)).expanduser()
|
|
78
|
+
home = path.parents[2] if len(path.parents) >= 3 else Path.home()
|
|
79
|
+
return MuseExecutable(
|
|
80
|
+
str(executable),
|
|
81
|
+
muse_path=str(executable),
|
|
82
|
+
transcript_root=home / ".local" / "share" / "muse",
|
|
83
|
+
)
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
def _wsl_executable(self) -> MuseExecutable | None:
|
|
87
|
+
if self.process.platform_name != "nt":
|
|
88
|
+
return None
|
|
89
|
+
wsl = self.process.find_executable("wsl.exe") or self.process.find_executable("wsl")
|
|
90
|
+
if not wsl:
|
|
91
|
+
return None
|
|
92
|
+
result = self.process.run(
|
|
93
|
+
[wsl, "-e", "sh", "-lc", "command -v muse"],
|
|
94
|
+
check=False,
|
|
95
|
+
capture_output=True,
|
|
96
|
+
text=True,
|
|
97
|
+
)
|
|
98
|
+
if result.returncode:
|
|
99
|
+
return None
|
|
100
|
+
muse_path = str(result.stdout or "").strip().splitlines()
|
|
101
|
+
if not muse_path:
|
|
102
|
+
return None
|
|
103
|
+
path = muse_path[-1].strip()
|
|
104
|
+
if not path.startswith("/"):
|
|
105
|
+
return None
|
|
106
|
+
root_result = self.process.run(
|
|
107
|
+
[wsl, "-e", "sh", "-lc", 'wslpath -w "$HOME/.local/share/muse"'],
|
|
108
|
+
check=False,
|
|
109
|
+
capture_output=True,
|
|
110
|
+
text=True,
|
|
111
|
+
)
|
|
112
|
+
root_lines = str(root_result.stdout or "").strip().splitlines()
|
|
113
|
+
transcript_root = (
|
|
114
|
+
Path(root_lines[-1].strip())
|
|
115
|
+
if root_result.returncode == 0 and root_lines
|
|
116
|
+
else None
|
|
117
|
+
)
|
|
118
|
+
return MuseExecutable(
|
|
119
|
+
str(wsl),
|
|
120
|
+
(
|
|
121
|
+
"-e",
|
|
122
|
+
"env",
|
|
123
|
+
"-u",
|
|
124
|
+
"META_API_KEY",
|
|
125
|
+
"-u",
|
|
126
|
+
"MODEL_API_KEY",
|
|
127
|
+
path,
|
|
128
|
+
),
|
|
129
|
+
"wsl",
|
|
130
|
+
path,
|
|
131
|
+
transcript_root,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def discover(self) -> MuseExecutable | None:
|
|
135
|
+
return self._native_executable() or self._wsl_executable()
|
|
136
|
+
|
|
137
|
+
def install_if_missing(self) -> MuseExecutable | None:
|
|
138
|
+
installed = self.discover()
|
|
139
|
+
if installed:
|
|
140
|
+
return installed
|
|
141
|
+
shell = self.process.find_executable("bash")
|
|
142
|
+
command = f"curl -fsSL {MUSE_INSTALL_URL} | bash"
|
|
143
|
+
if self.process.platform_name == "nt":
|
|
144
|
+
wsl = self.process.find_executable("wsl.exe") or self.process.find_executable("wsl")
|
|
145
|
+
if not wsl:
|
|
146
|
+
self.process.print_line(
|
|
147
|
+
"Muse Code supports macOS and Linux. Install WSL2, then run: "
|
|
148
|
+
f"wsl bash -lc '{command}'",
|
|
149
|
+
flush=True,
|
|
150
|
+
)
|
|
151
|
+
return None
|
|
152
|
+
install_command = [wsl, "-e", "bash", "-lc", command]
|
|
153
|
+
elif shell:
|
|
154
|
+
install_command = [shell, "-lc", command]
|
|
155
|
+
else:
|
|
156
|
+
self.process.print_line(
|
|
157
|
+
f"Muse Code is missing. Install it with: {command}", flush=True
|
|
158
|
+
)
|
|
159
|
+
return None
|
|
160
|
+
self.process.print_line("Installing Muse Code from Meta's official installer...", flush=True)
|
|
161
|
+
result = self.process.run(install_command, check=False)
|
|
162
|
+
if result.returncode:
|
|
163
|
+
self.process.print_line(
|
|
164
|
+
f"Muse Code installation failed (exit {result.returncode}).", flush=True
|
|
165
|
+
)
|
|
166
|
+
return None
|
|
167
|
+
return self.discover()
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def _model(provider: str, provider_config: dict[str, Any]) -> str:
|
|
171
|
+
configured = str(provider_config.get("current_model") or "").strip()
|
|
172
|
+
if provider == "meta" and configured.startswith("muse-"):
|
|
173
|
+
return configured
|
|
174
|
+
return "muse-spark-1.3"
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def _effort(provider: str, provider_config: dict[str, Any]) -> str:
|
|
178
|
+
if provider != "meta":
|
|
179
|
+
return ""
|
|
180
|
+
configured = str(provider_config.get("effort_level") or "").strip().lower()
|
|
181
|
+
configured = {"max": "ultra"}.get(configured, configured)
|
|
182
|
+
if configured in {"none", "minimal", "low", "medium", "high", "xhigh", "ultra"}:
|
|
183
|
+
return configured
|
|
184
|
+
return ""
|
|
185
|
+
|
|
186
|
+
def launch(self, passthrough: list[str]) -> int:
|
|
187
|
+
argv = list(passthrough)
|
|
188
|
+
executable = self.install_if_missing()
|
|
189
|
+
if executable is None:
|
|
190
|
+
return 127
|
|
191
|
+
config = self.config.load()
|
|
192
|
+
provider, provider_config = self.config.current_provider(config)
|
|
193
|
+
model = self._model(provider, provider_config)
|
|
194
|
+
options: dict[str, Any] = {"prefix_args": executable.prefix_args}
|
|
195
|
+
if not has_option(argv, "--yolo"):
|
|
196
|
+
options["yolo_args"] = ("--yolo",)
|
|
197
|
+
muse_provider = option_value(argv, "--provider").lower()
|
|
198
|
+
meta_launch = not muse_provider or muse_provider == "meta"
|
|
199
|
+
if meta_launch and model and not has_option(argv, "-m", "--model"):
|
|
200
|
+
options["model"] = model
|
|
201
|
+
effort = self._effort(provider, provider_config)
|
|
202
|
+
if meta_launch and effort and not has_option(argv, "--reasoning-effort"):
|
|
203
|
+
options["reasoning_effort"] = effort
|
|
204
|
+
|
|
205
|
+
env = self.process.environment.copy()
|
|
206
|
+
env["PATH"] = self.process.augment_path(env)
|
|
207
|
+
for name in MUSE_SUBSCRIPTION_ENV_KEYS:
|
|
208
|
+
env.pop(name, None)
|
|
209
|
+
command, child_env = self.lifecycle.materialize_command(
|
|
210
|
+
"muse",
|
|
211
|
+
executable.command,
|
|
212
|
+
env,
|
|
213
|
+
provider,
|
|
214
|
+
provider_config,
|
|
215
|
+
mode="native",
|
|
216
|
+
protocol="native",
|
|
217
|
+
cwd=Path.cwd(),
|
|
218
|
+
enable_channels=True,
|
|
219
|
+
passthrough=argv,
|
|
220
|
+
options=options,
|
|
221
|
+
)
|
|
222
|
+
non_session_commands = {
|
|
223
|
+
"--help", "-h", "--version", "-V", "-v", "version", "config", "export",
|
|
224
|
+
"trace", "skills", "sandbox", "schema", "serve", "session-message",
|
|
225
|
+
"auth", "login", "logout", "init",
|
|
226
|
+
}
|
|
227
|
+
is_session = not argv or argv[0] not in non_session_commands
|
|
228
|
+
interactive_session = is_session and (not argv or argv[0] != "exec")
|
|
229
|
+
if is_session:
|
|
230
|
+
self.lifecycle.record_launch(provider, model)
|
|
231
|
+
if interactive_session:
|
|
232
|
+
self.lifecycle.set_transcript_scope(
|
|
233
|
+
"muse",
|
|
234
|
+
cwd=Path.cwd(),
|
|
235
|
+
muse_home=executable.transcript_root,
|
|
236
|
+
)
|
|
237
|
+
manage_router = bool(
|
|
238
|
+
interactive_session
|
|
239
|
+
and (
|
|
240
|
+
self.lifecycle.channel_delivery_mode(config) == "llm"
|
|
241
|
+
or self.lifecycle.web_backend_requested(config)
|
|
242
|
+
)
|
|
243
|
+
and self.lifecycle.start_router()
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def run() -> int:
|
|
247
|
+
if not interactive_session:
|
|
248
|
+
return self.process.call(command, env=child_env)
|
|
249
|
+
return self.lifecycle.call_with_channel_proxy(
|
|
250
|
+
command,
|
|
251
|
+
child_env,
|
|
252
|
+
wake_for_llm_delivery=False,
|
|
253
|
+
synthetic_enter_bytes=None,
|
|
254
|
+
normalize_bare_cr_for_synthetic_enter=False,
|
|
255
|
+
# Muse's session JSONL uses a different schema from the
|
|
256
|
+
# Claude/Codex turn-confirmation reader. Keep delivery
|
|
257
|
+
# fire-and-forget while the tool-event observer tails it.
|
|
258
|
+
channel_wake_submit_retries=1,
|
|
259
|
+
channel_wake_confirm_submit=False,
|
|
260
|
+
channel_wake_bracketed_paste=True,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return self.lifecycle.run_with_router(run, manage_router)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclass(frozen=True, slots=True)
|
|
267
|
+
class MuseRuntimeCompatibilityApi:
|
|
268
|
+
context: Callable[[], MuseRuntimeContext]
|
|
269
|
+
|
|
270
|
+
def discover(self) -> MuseExecutable | None:
|
|
271
|
+
return self.context().discover()
|
|
272
|
+
|
|
273
|
+
def install_if_missing(self) -> MuseExecutable | None:
|
|
274
|
+
return self.context().install_if_missing()
|
|
275
|
+
|
|
276
|
+
def launch(self, passthrough: list[str]) -> int:
|
|
277
|
+
return self.context().launch(passthrough)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
__all__ = [
|
|
281
|
+
"MUSE_INSTALL_URL",
|
|
282
|
+
"MUSE_SUBSCRIPTION_ENV_KEYS",
|
|
283
|
+
"MuseConfigurationPorts",
|
|
284
|
+
"MuseExecutable",
|
|
285
|
+
"MuseLifecyclePorts",
|
|
286
|
+
"MuseProcessPorts",
|
|
287
|
+
"MuseRuntimeCompatibilityApi",
|
|
288
|
+
"MuseRuntimeContext",
|
|
289
|
+
"has_option",
|
|
290
|
+
"option_value",
|
|
291
|
+
]
|
|
@@ -25,22 +25,6 @@ class OllamaReasoningProfile:
|
|
|
25
25
|
always_on: bool = False
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
_GENERIC_CLOUD_LEVEL_MODELS = frozenset(
|
|
29
|
-
{
|
|
30
|
-
"gemma4:31b",
|
|
31
|
-
"glm-5.1",
|
|
32
|
-
"kimi-k2.6",
|
|
33
|
-
"kimi-k2.7-code",
|
|
34
|
-
"minimax-m2.7",
|
|
35
|
-
"minimax-m3",
|
|
36
|
-
"nemotron-3-nano:30b",
|
|
37
|
-
"nemotron-3-super",
|
|
38
|
-
"nemotron-3-ultra",
|
|
39
|
-
"qwen3.5:397b",
|
|
40
|
-
}
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
28
|
def _codex_level_description(level: str) -> str:
|
|
45
29
|
return {
|
|
46
30
|
"low": "Low reasoning effort",
|
|
@@ -107,6 +91,14 @@ def ollama_cloud_reasoning_profile(
|
|
|
107
91
|
default_codex="high",
|
|
108
92
|
always_on=True,
|
|
109
93
|
)
|
|
94
|
+
if discovered == "glm_dsa_moe" or model == "glm-5.3":
|
|
95
|
+
return OllamaReasoningProfile(
|
|
96
|
+
native_levels=("low", "high", "max"),
|
|
97
|
+
codex_levels=("low", "high", "xhigh"),
|
|
98
|
+
default_native="max",
|
|
99
|
+
default_codex="xhigh",
|
|
100
|
+
always_on=True,
|
|
101
|
+
)
|
|
110
102
|
if discovered == "kimi-k3" or model == "kimi-k3":
|
|
111
103
|
return OllamaReasoningProfile(
|
|
112
104
|
native_levels=("low", "high", "max"),
|
|
@@ -124,13 +116,6 @@ def ollama_cloud_reasoning_profile(
|
|
|
124
116
|
default_native=native_default,
|
|
125
117
|
default_codex="xhigh" if native_default == "max" else native_default,
|
|
126
118
|
)
|
|
127
|
-
if model in _GENERIC_CLOUD_LEVEL_MODELS:
|
|
128
|
-
return OllamaReasoningProfile(
|
|
129
|
-
native_levels=("low", "medium", "high", "max"),
|
|
130
|
-
codex_levels=("low", "medium", "high", "xhigh"),
|
|
131
|
-
default_native="medium",
|
|
132
|
-
default_codex="medium",
|
|
133
|
-
)
|
|
134
119
|
return None
|
|
135
120
|
|
|
136
121
|
|
|
@@ -185,7 +170,7 @@ def ollama_cloud_model_config_updates(
|
|
|
185
170
|
),
|
|
186
171
|
}
|
|
187
172
|
return {
|
|
188
|
-
"think": profile is not None,
|
|
173
|
+
"think": profile is not None or "thinking" in normalized_capabilities,
|
|
189
174
|
"effort_level": profile.default_native if profile else "",
|
|
190
175
|
"ollama_think_levels": list(profile.native_levels) if profile else [],
|
|
191
176
|
"ollama_thinking_always_on": bool(profile and profile.always_on),
|
|
@@ -246,6 +231,8 @@ class OllamaThinkingPolicy:
|
|
|
246
231
|
return "gptoss"
|
|
247
232
|
if model in {"glm-5.2", "glm-5.2:cloud"}:
|
|
248
233
|
return "glm5.2"
|
|
234
|
+
if model in {"glm-5.3", "glm-5.3:cloud"}:
|
|
235
|
+
return "glm_dsa_moe"
|
|
249
236
|
return ""
|
|
250
237
|
|
|
251
238
|
def value(
|
|
@@ -342,8 +329,7 @@ class OllamaThinkingPolicy:
|
|
|
342
329
|
if "thinking" in capabilities:
|
|
343
330
|
if thinking_disabled(request):
|
|
344
331
|
return False
|
|
345
|
-
|
|
346
|
-
return True
|
|
332
|
+
return True
|
|
347
333
|
return None
|
|
348
334
|
|
|
349
335
|
|
|
@@ -82,6 +82,7 @@ class PrelaunchLaunchPolicy:
|
|
|
82
82
|
launch_kimi: Callable[..., Any]
|
|
83
83
|
launch_grok: Callable[..., Any] = lambda *_args, **_kwargs: 0
|
|
84
84
|
launch_zcode: Callable[..., Any] = lambda *_args, **_kwargs: 0
|
|
85
|
+
launch_muse: Callable[..., Any] = lambda *_args, **_kwargs: 0
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
@dataclass(frozen=True, slots=True)
|
|
@@ -223,6 +224,7 @@ def run_prelaunch_menu(passthrough: list[str] | None = None,
|
|
|
223
224
|
launch_kimi = services.launch_policy.launch_kimi
|
|
224
225
|
launch_grok = services.launch_policy.launch_grok
|
|
225
226
|
launch_zcode = services.launch_policy.launch_zcode
|
|
227
|
+
launch_muse = services.launch_policy.launch_muse
|
|
226
228
|
llm_option_current_bool = services.options.llm_option_current_bool
|
|
227
229
|
llm_option_panel_rows = services.panel_rows.llm_option_panel_rows
|
|
228
230
|
llm_option_prompt_default = services.options.llm_option_prompt_default
|
|
@@ -500,6 +502,10 @@ def run_prelaunch_menu(passthrough: list[str] | None = None,
|
|
|
500
502
|
persist_launch_action(action)
|
|
501
503
|
launch_zcode([])
|
|
502
504
|
return PRELAUNCH_CANCEL
|
|
505
|
+
if action == "launch-muse":
|
|
506
|
+
persist_launch_action(action)
|
|
507
|
+
launch_muse([])
|
|
508
|
+
return PRELAUNCH_CANCEL
|
|
503
509
|
continue
|
|
504
510
|
if panel == "language" and value:
|
|
505
511
|
cfg["language"] = value
|
|
@@ -770,6 +776,13 @@ def run_prelaunch_menu(passthrough: list[str] | None = None,
|
|
|
770
776
|
continue
|
|
771
777
|
if value in {"enabled", "transport"}:
|
|
772
778
|
messages = set_external_event_config(value, "toggle")
|
|
779
|
+
elif value == "input_transport":
|
|
780
|
+
entered = prompt_menu_value(
|
|
781
|
+
"Runtime input transport (auto/session_socket/tty/router)",
|
|
782
|
+
restore_tty=restore_line_mode,
|
|
783
|
+
raw_tty=restore_raw_mode,
|
|
784
|
+
)
|
|
785
|
+
messages = set_external_event_config(value, entered or "auto")
|
|
773
786
|
elif value == "url":
|
|
774
787
|
entered = prompt_menu_value(
|
|
775
788
|
"SSE URL (blank for webhook mode)",
|
|
@@ -28,10 +28,11 @@ def launch_panel_rows(
|
|
|
28
28
|
f"Kimi{kimi_suffix}",
|
|
29
29
|
"Grok Build",
|
|
30
30
|
"ZCode",
|
|
31
|
+
"Muse Code (subscription)",
|
|
31
32
|
f"Codex app server{codex_suffix}",
|
|
32
33
|
"Back",
|
|
33
34
|
],
|
|
34
|
-
["launch", "launch-codex", "launch-agy", "launch-kimi", "launch-grok", "launch-zcode", "launch-codex-app-server", "back"],
|
|
35
|
+
["launch", "launch-codex", "launch-agy", "launch-kimi", "launch-grok", "launch-zcode", "launch-muse", "launch-codex-app-server", "back"],
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
|