cdx-manager 0.21.5 → 0.22.0
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/README.md +1 -1
- package/VERSION +1 -1
- package/changelogs/CHANGELOGS_0_21_6.md +33 -0
- package/changelogs/CHANGELOGS_0_22_0.md +25 -0
- package/checksums/release-archives.json +22 -0
- package/docs/cli.md +17 -7
- package/docs/operations.md +80 -0
- package/docs/sessions-and-notifications.md +1 -1
- package/docs/technical-overview.md +1 -1
- package/install.ps1 +7 -4
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/agent_notify.py +23 -7
- package/src/backup_bundle.py +60 -16
- package/src/claude_usage.py +1 -1
- package/src/cli.py +15 -4
- package/src/cli_args.py +76 -19
- package/src/cli_helpers.py +23 -9
- package/src/codex_usage.py +1 -1
- package/src/commands/launch.py +41 -3
- package/src/commands/maintenance.py +1 -0
- package/src/commands/rescue.py +207 -0
- package/src/commands/runs.py +26 -3
- package/src/commands/status.py +56 -6
- package/src/config.py +17 -1
- package/src/context_store.py +11 -6
- package/src/errors.py +5 -1
- package/src/handoff.py +22 -11
- package/src/health.py +2 -2
- package/src/interactive_usage.py +20 -5
- package/src/notify.py +23 -18
- package/src/provider_background.py +13 -8
- package/src/provider_runtime.py +101 -8
- package/src/retro.py +131 -0
- package/src/rtk_savings.py +68 -0
- package/src/run_registry.py +27 -1
- package/src/run_usage.py +36 -9
- package/src/session_backup.py +16 -5
- package/src/session_helpers.py +4 -0
- package/src/session_ranking.py +10 -0
- package/src/session_service.py +39 -34
- package/src/session_status.py +86 -2
- package/src/shared_memory.py +102 -0
- package/src/status_source.py +2 -0
- package/src/status_view.py +23 -23
- package/src/tray_autostart.py +21 -2
- package/src/tray_events.py +23 -4
- package/src/tray_instance.py +11 -2
- package/src/tray_logics.py +1 -1
- package/src/update_all.py +1 -1
- package/src/update_check.py +1 -1
- package/src/update_manager.py +17 -7
package/src/cli.py
CHANGED
|
@@ -6,6 +6,7 @@ import sys
|
|
|
6
6
|
from datetime import datetime, timezone
|
|
7
7
|
|
|
8
8
|
from .agent_notify import handle_notify
|
|
9
|
+
from .cli_args import SET_USAGE
|
|
9
10
|
from .cli_commands import (
|
|
10
11
|
API_SCHEMA_VERSION,
|
|
11
12
|
STATUS_USAGE,
|
|
@@ -70,8 +71,10 @@ from .cli_render import (
|
|
|
70
71
|
_visible_len,
|
|
71
72
|
format_error,
|
|
72
73
|
)
|
|
74
|
+
from .commands.rescue import handle_rescue
|
|
73
75
|
from .config import PROVIDER_OLLAMA
|
|
74
76
|
from .errors import CdxError
|
|
77
|
+
from .retro import handle_retro
|
|
75
78
|
from .session_service import create_session_service
|
|
76
79
|
from .status_view import (
|
|
77
80
|
_format_blocking_quota,
|
|
@@ -174,6 +177,8 @@ _COMMAND_HANDLERS = {
|
|
|
174
177
|
"rename": handle_rename,
|
|
175
178
|
"resume": handle_resume,
|
|
176
179
|
"can-resume": handle_can_resume,
|
|
180
|
+
"rescue": handle_rescue,
|
|
181
|
+
"retro": handle_retro,
|
|
177
182
|
"rmv": handle_remove,
|
|
178
183
|
"run": handle_run,
|
|
179
184
|
"run-report": handle_run_report,
|
|
@@ -235,7 +240,7 @@ def _print_help(use_color=False):
|
|
|
235
240
|
f" {_style('cdx reset <name> [--yes] [--json]', '36', use_color)}",
|
|
236
241
|
f" {_style('cdx auth refresh <name|all> [--json]', '36', use_color)}",
|
|
237
242
|
f" {_style('cdx select --provider PROVIDER [--min-reasoning-effort minimal|low|medium|high|xhigh] [--min-power minimal|low|medium|high|xhigh] [--require-ready] [--refresh] --json', '36', use_color)}",
|
|
238
|
-
f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] [--detach] [--refresh] --json', '36', use_color)}",
|
|
243
|
+
f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review|retro] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] [--label TEXT] [--max-parallel N] [--detach] [--failover] [--refresh] --json', '36', use_color)}",
|
|
239
244
|
f" {_style('cdx runs [--limit N] [--since 7d|today|DATE] --json', '36', use_color)}",
|
|
240
245
|
f" {_style('cdx run-status <run_id> --json', '36', use_color)}",
|
|
241
246
|
f" {_style('cdx run-report <run_id> --json', '36', use_color)}",
|
|
@@ -247,13 +252,15 @@ def _print_help(use_color=False):
|
|
|
247
252
|
f" {_style('cdx config <name> [--json]', '36', use_color)}",
|
|
248
253
|
f" {_style('cdx configs [--json]', '36', use_color)}",
|
|
249
254
|
f" {_style('cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]', '36', use_color)}",
|
|
250
|
-
f" {_style('
|
|
255
|
+
f" {_style(SET_USAGE[len('Usage: '):], '36', use_color)}",
|
|
251
256
|
f" {_style('cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--logics|--notify|--notify-preview|--model|--priority|--all) [--json]', '36', use_color)}",
|
|
252
257
|
f" {_style('cdx history [name] [--limit N] [--summary] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]', '36', use_color)}",
|
|
253
|
-
f" {_style('cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]', '36', use_color)}",
|
|
258
|
+
f" {_style('cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--by session|project|model] [--json]', '36', use_color)}",
|
|
254
259
|
f" {_style('cdx last [--json]', '36', use_color)}",
|
|
255
260
|
f" {_style('cdx resume <name> [--json]', '36', use_color)}",
|
|
256
261
|
f" {_style('cdx can-resume <name> [--json]', '36', use_color)}",
|
|
262
|
+
f" {_style('cdx rescue [<session>] [--to <target>] [--yes] [--dry-run] [--json]', '36', use_color)}",
|
|
263
|
+
f" {_style('cdx retro [--limit N] [--json]', '36', use_color)}",
|
|
257
264
|
f" {_style('cdx handoff <name> [--json]', '36', use_color)}",
|
|
258
265
|
f" {_style('cdx handoff <source> <target> [--source-conversation ID | --source-native-transcript PATH | --source-transcript PATH] [--json]', '36', use_color)}",
|
|
259
266
|
f" {_style('cdx add [provider] <name> [--model MODEL] [--json]', '36', use_color)}",
|
|
@@ -284,6 +291,7 @@ def _print_help(use_color=False):
|
|
|
284
291
|
f" {_style('cdx ready', '36', use_color)} # next session to become ready",
|
|
285
292
|
f" {_style('cdx <name> [--json]', '36', use_color)}",
|
|
286
293
|
f" {_style('cdx <name> --prompt TEXT|--prompt-file PATH', '36', use_color)} # start with a first message",
|
|
294
|
+
f" {_style('cdx <name> --dir PATH --task TEXT --end-state TEXT', '36', use_color)} # start on a task, with what the go-ahead covers",
|
|
287
295
|
f" {_style('cdx --help', '36', use_color)}",
|
|
288
296
|
f" {_style('cdx --version', '36', use_color)}",
|
|
289
297
|
])
|
|
@@ -533,7 +541,6 @@ def main(argv, options=None):
|
|
|
533
541
|
stderr = options.get("stderr", sys.stderr)
|
|
534
542
|
stdin_is_tty = options.get("stdin", {}).get("isTTY", hasattr(sys.stdin, "isatty") and sys.stdin.isatty())
|
|
535
543
|
use_color = _should_use_color(env, stdout)
|
|
536
|
-
service = options.get("service") or create_session_service({"env": env})
|
|
537
544
|
spawn = options.get("spawn")
|
|
538
545
|
spawn_sync = options.get("spawn_sync")
|
|
539
546
|
refresh_fn = options.get("refreshClaudeSessionStatus")
|
|
@@ -559,6 +566,10 @@ def main(argv, options=None):
|
|
|
559
566
|
out(f"{_print_version()}\n")
|
|
560
567
|
return 0
|
|
561
568
|
|
|
569
|
+
# Built after --help/--version: those must answer even where the home
|
|
570
|
+
# directory cannot be resolved (a service or task with a minimal environment).
|
|
571
|
+
service = options.get("service") or create_session_service({"env": env})
|
|
572
|
+
|
|
562
573
|
if argv == ["--json"]:
|
|
563
574
|
rows = service["format_list_rows"]()
|
|
564
575
|
notices = _get_update_notices(service, env, options)
|
package/src/cli_args.py
CHANGED
|
@@ -16,6 +16,7 @@ from .config import (
|
|
|
16
16
|
PROVIDERS,
|
|
17
17
|
REASONING_EFFORT_VALUES,
|
|
18
18
|
normalize_permission,
|
|
19
|
+
parse_on_off,
|
|
19
20
|
)
|
|
20
21
|
from .errors import CdxArgumentError, CdxError
|
|
21
22
|
from .provider_runtime import _normalize_reasoning_effort
|
|
@@ -32,26 +33,27 @@ CONTEXT_USAGE = "Usage: cdx context show|path|init|edit|clear|set|append [text..
|
|
|
32
33
|
MEMORY_USAGE = "Usage: cdx memory [--global|--project NAME_OR_PATH] [show|view|path|init|edit|clear|set|append|list] [text...] [--json]"
|
|
33
34
|
HANDOFF_USAGE = "Usage: cdx handoff <name> [--json] | cdx handoff <source> <target> [--source-conversation ID | --source-native-transcript PATH | --source-transcript PATH] [--json]"
|
|
34
35
|
LABEL_USAGE = "Usage: cdx label <name> <label> [--json] | cdx label <name> --clear [--json]"
|
|
35
|
-
SET_USAGE = "Usage: cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--logics on|off] [--model MODEL] [--fallback-model MODEL[,MODEL...]] [--budget USD] [--extra-args 'ARGS'] [--priority 0..100] [--json]"
|
|
36
|
-
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--reasoning-effort|--permission|--fast|--rtk|--logics|--model|--fallback-model|--budget|--extra-args|--priority|--all) [--json]"
|
|
36
|
+
SET_USAGE = "Usage: cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--logics on|off] [--notify on|off] [--notify-preview on|off] [--scrub-env on|off] [--shared-memory on|off] [--retro on|off] [--policy use|ask-first|never] [--model MODEL] [--fallback-model MODEL[,MODEL...]] [--budget USD] [--extra-args 'ARGS'] [--priority 0..100] [--json]"
|
|
37
|
+
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--reasoning-effort|--permission|--fast|--rtk|--logics|--notify|--notify-preview|--scrub-env|--shared-memory|--retro|--policy|--model|--fallback-model|--budget|--extra-args|--priority|--all) [--json]"
|
|
37
38
|
SETTING_ALIAS_USAGE = "Usage: cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]"
|
|
38
39
|
CONFIG_USAGE = "Usage: cdx config <name> [--json]"
|
|
39
40
|
CONFIGS_USAGE = "Usage: cdx configs [--json]"
|
|
40
41
|
HISTORY_USAGE = "Usage: cdx history [name] [--limit N] [--summary] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]"
|
|
41
|
-
STATS_USAGE = "Usage: cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]"
|
|
42
|
+
STATS_USAGE = "Usage: cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--by session|project|model] [--json]"
|
|
43
|
+
STATS_GROUPS = ("session", "project", "model")
|
|
42
44
|
LAST_USAGE = "Usage: cdx last [--json]"
|
|
43
45
|
RESUME_USAGE = "Usage: cdx resume <name> [--json]"
|
|
44
46
|
CAN_RESUME_USAGE = "Usage: cdx can-resume <name> [--json]"
|
|
45
47
|
SELECT_USAGE = "Usage: cdx select --provider PROVIDER [--min-reasoning-effort minimal|low|medium|high|xhigh] [--min-power minimal|low|medium|high|xhigh] [--require-ready] [--refresh] --json"
|
|
46
48
|
NEXT_USAGE = "Usage: cdx next [--json] [--refresh]"
|
|
47
|
-
RUN_USAGE = "Usage: cdx run [session] (--cwd|--dir) PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] [--detach] [--failover] [--refresh] --json"
|
|
49
|
+
RUN_USAGE = "Usage: cdx run [session] (--cwd|--dir) PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review|retro] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] [--label TEXT] [--max-parallel N] [--detach] [--failover] [--refresh] --json"
|
|
48
50
|
RUN_JSON_REQUIRED = "cdx run: --json is required."
|
|
49
51
|
RUN_TARGET_REQUIRED = "cdx run: specify a session name or --provider PROVIDER."
|
|
50
52
|
RUN_SESSION_PROVIDER_CONFLICT = "cdx run: cannot specify both a session name and --provider."
|
|
51
53
|
RUN_CWD_REQUIRED = "cdx run: --cwd PATH is required."
|
|
52
54
|
RUN_PROMPT_SOURCE_REQUIRED = "cdx run: specify exactly one prompt source: --prompt TEXT or --prompt-file PATH."
|
|
53
55
|
RUN_DETACH_FAILOVER_CONFLICT = "cdx run: --failover needs to watch the run, which --detach gives up."
|
|
54
|
-
RUN_KIND_VALUES = ("assistant", "code-review")
|
|
56
|
+
RUN_KIND_VALUES = ("assistant", "code-review", "retro")
|
|
55
57
|
# Aliases of the shared definitions in config, kept only so the existing
|
|
56
58
|
# RUN_* spellings in this module keep reading naturally. They are references,
|
|
57
59
|
# not copies: config.py is the single owner.
|
|
@@ -213,13 +215,12 @@ def _parse_toggle_args(args, usage):
|
|
|
213
215
|
return {"name": parsed["names"][0], "json": parsed["json"]}
|
|
214
216
|
|
|
215
217
|
|
|
216
|
-
def _parse_fast_value(value):
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
raise CdxError(SET_USAGE)
|
|
218
|
+
def _parse_fast_value(value, label="fast"):
|
|
219
|
+
return parse_on_off(value, label)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _on_off(label):
|
|
223
|
+
return lambda value: parse_on_off(value, label)
|
|
223
224
|
|
|
224
225
|
|
|
225
226
|
def _parse_priority_value(value):
|
|
@@ -248,15 +249,19 @@ def _parse_set_args(args):
|
|
|
248
249
|
"--power": {"key": "power", "type": "str", "default": None},
|
|
249
250
|
"--permission": {"key": "permission", "type": "str", "default": None},
|
|
250
251
|
"--fast": {"key": "fast", "type": "str", "default": None, "transform": _parse_fast_value},
|
|
251
|
-
"--rtk": {"key": "rtk", "type": "str", "default": None, "transform":
|
|
252
|
-
"--logics": {"key": "logics", "type": "str", "default": None, "transform":
|
|
253
|
-
"--notify": {"key": "notify", "type": "str", "default": None, "transform":
|
|
254
|
-
"--notify-preview": {"key": "notify_preview", "type": "str", "default": None, "transform":
|
|
252
|
+
"--rtk": {"key": "rtk", "type": "str", "default": None, "transform": _on_off("rtk")},
|
|
253
|
+
"--logics": {"key": "logics", "type": "str", "default": None, "transform": _on_off("logics")},
|
|
254
|
+
"--notify": {"key": "notify", "type": "str", "default": None, "transform": _on_off("notify")},
|
|
255
|
+
"--notify-preview": {"key": "notify_preview", "type": "str", "default": None, "transform": _on_off("notify preview")},
|
|
256
|
+
"--scrub-env": {"key": "scrub_env", "type": "str", "default": None, "transform": _on_off("scrub env")},
|
|
257
|
+
"--shared-memory": {"key": "shared_memory", "type": "str", "default": None, "transform": _on_off("shared memory")},
|
|
258
|
+
"--retro": {"key": "retro", "type": "str", "default": None, "transform": _on_off("retro")},
|
|
255
259
|
"--model": {"key": "model", "type": "str", "default": None},
|
|
256
260
|
"--fallback-model": {"key": "fallback_model", "type": "str", "default": None},
|
|
257
261
|
"--budget": {"key": "budget", "type": "str", "default": None, "transform": _parse_budget_value},
|
|
258
262
|
"--extra-args": {"key": "extra_args", "type": "str", "default": None},
|
|
259
263
|
"--priority": {"key": "priority", "type": "str", "default": None, "transform": _parse_priority_value},
|
|
264
|
+
"--policy": {"key": "policy", "type": "str", "default": None},
|
|
260
265
|
"--sessions": {"key": "sessions", "type": "str", "default": None, "transform": _parse_set_unset_sessions},
|
|
261
266
|
"--provider": {"key": "provider", "type": "str", "default": None, "transform": lambda value: _parse_provider_filter(value, SET_USAGE)},
|
|
262
267
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
@@ -273,7 +278,7 @@ def _parse_set_args(args):
|
|
|
273
278
|
raise CdxError(SET_USAGE)
|
|
274
279
|
settings = {
|
|
275
280
|
key: parsed[key]
|
|
276
|
-
for key in ("power", "permission", "fast", "rtk", "logics", "notify", "notify_preview", "model", "fallback_model", "budget", "extra_args", "priority")
|
|
281
|
+
for key in ("power", "permission", "fast", "rtk", "logics", "notify", "notify_preview", "scrub_env", "shared_memory", "retro", "model", "fallback_model", "budget", "extra_args", "priority", "policy")
|
|
277
282
|
if parsed[key] is not None
|
|
278
283
|
}
|
|
279
284
|
if not settings:
|
|
@@ -297,11 +302,15 @@ def _parse_unset_args(args):
|
|
|
297
302
|
"--logics": {"key": "logics", "type": "bool", "default": False},
|
|
298
303
|
"--notify": {"key": "notify", "type": "bool", "default": False},
|
|
299
304
|
"--notify-preview": {"key": "notify_preview", "type": "bool", "default": False},
|
|
305
|
+
"--scrub-env": {"key": "scrub_env", "type": "bool", "default": False},
|
|
306
|
+
"--shared-memory": {"key": "shared_memory", "type": "bool", "default": False},
|
|
307
|
+
"--retro": {"key": "retro", "type": "bool", "default": False},
|
|
300
308
|
"--model": {"key": "model", "type": "bool", "default": False},
|
|
301
309
|
"--fallback-model": {"key": "fallback_model", "type": "bool", "default": False},
|
|
302
310
|
"--budget": {"key": "budget", "type": "bool", "default": False},
|
|
303
311
|
"--extra-args": {"key": "extra_args", "type": "bool", "default": False},
|
|
304
312
|
"--priority": {"key": "priority", "type": "bool", "default": False},
|
|
313
|
+
"--policy": {"key": "policy", "type": "bool", "default": False},
|
|
305
314
|
"--all": {"key": "all", "type": "bool", "default": False},
|
|
306
315
|
"--sessions": {"key": "sessions", "type": "str", "default": None, "transform": _parse_set_unset_sessions},
|
|
307
316
|
"--provider": {"key": "provider", "type": "str", "default": None, "transform": lambda value: _parse_provider_filter(value, UNSET_USAGE)},
|
|
@@ -318,8 +327,8 @@ def _parse_unset_args(args):
|
|
|
318
327
|
if not parsed["names"] and not parsed["sessions"] and not parsed["provider"]:
|
|
319
328
|
raise CdxError(UNSET_USAGE)
|
|
320
329
|
_UNSET_KEYS = (
|
|
321
|
-
"power", "reasoning_effort", "permission", "fast", "rtk", "logics", "notify", "notify_preview",
|
|
322
|
-
"model", "fallback_model", "budget", "extra_args", "priority",
|
|
330
|
+
"power", "reasoning_effort", "permission", "fast", "rtk", "logics", "notify", "notify_preview", "scrub_env", "shared_memory", "retro",
|
|
331
|
+
"model", "fallback_model", "budget", "extra_args", "priority", "policy",
|
|
323
332
|
)
|
|
324
333
|
keys = list(_UNSET_KEYS) if parsed["all"] else [key for key in _UNSET_KEYS if parsed[key]]
|
|
325
334
|
if not keys:
|
|
@@ -510,8 +519,17 @@ def cdx_schema():
|
|
|
510
519
|
"provider_start_failed",
|
|
511
520
|
"provider_failed",
|
|
512
521
|
"provider_timeout",
|
|
522
|
+
"run_cancelled",
|
|
523
|
+
"too_many_runs",
|
|
524
|
+
"failover_exhausted",
|
|
525
|
+
"failover_limit_reached",
|
|
526
|
+
"invalid_request",
|
|
513
527
|
"cdx_error",
|
|
514
528
|
],
|
|
529
|
+
# The error code on a run record the stale sweep closed.
|
|
530
|
+
"run_record": [
|
|
531
|
+
"stale_process",
|
|
532
|
+
],
|
|
515
533
|
"run_lookup": [
|
|
516
534
|
"run_not_found",
|
|
517
535
|
"run_output_unavailable",
|
|
@@ -522,6 +540,26 @@ def cdx_schema():
|
|
|
522
540
|
"network_disabled_by_permission",
|
|
523
541
|
"session_selected_without_status",
|
|
524
542
|
"limit_ignored_with_since",
|
|
543
|
+
"claude_auth_probe_failed",
|
|
544
|
+
"claude_refresh_failed",
|
|
545
|
+
"disk_cleanup_available",
|
|
546
|
+
"update_available",
|
|
547
|
+
"logics_manager_update_available",
|
|
548
|
+
"update_version_check_failed",
|
|
549
|
+
"update_version_mismatch",
|
|
550
|
+
"logics_manager_missing",
|
|
551
|
+
"logics_viewer_failed",
|
|
552
|
+
"logics_viewer_root_invalid",
|
|
553
|
+
"tray_autostart_unsupported",
|
|
554
|
+
"tray_companion_missing",
|
|
555
|
+
"tray_companion_not_aligned",
|
|
556
|
+
"tray_companion_not_available",
|
|
557
|
+
"tray_companion_not_installed",
|
|
558
|
+
"tray_companion_restart_pending",
|
|
559
|
+
"tray_companion_updated",
|
|
560
|
+
"tray_plugin_action_unknown",
|
|
561
|
+
"tray_plugin_unknown",
|
|
562
|
+
"tray_terminal_invalid",
|
|
525
563
|
],
|
|
526
564
|
}
|
|
527
565
|
|
|
@@ -583,6 +621,17 @@ def _parse_run_effort(reasoning_effort, power):
|
|
|
583
621
|
) from error
|
|
584
622
|
|
|
585
623
|
|
|
624
|
+
def _parse_max_parallel(value):
|
|
625
|
+
try:
|
|
626
|
+
parsed = int(value)
|
|
627
|
+
except (TypeError, ValueError):
|
|
628
|
+
parsed = 0
|
|
629
|
+
if parsed < 1:
|
|
630
|
+
raise CdxArgumentError("cdx run: --max-parallel takes a whole number of at least 1.",
|
|
631
|
+
code=ARG_CODE_INVALID_VALUE, arguments=["--max-parallel"])
|
|
632
|
+
return parsed
|
|
633
|
+
|
|
634
|
+
|
|
586
635
|
def _parse_run_args(args):
|
|
587
636
|
parsed = _parse_run_flag_args(args, {
|
|
588
637
|
"--cwd": {"key": "cwd", "type": "str", "default": None},
|
|
@@ -599,6 +648,8 @@ def _parse_run_args(args):
|
|
|
599
648
|
"--detach": {"key": "detach", "type": "bool", "default": False},
|
|
600
649
|
"--failover": {"key": "failover", "type": "bool", "default": False},
|
|
601
650
|
"--refresh": {"key": "refresh", "type": "bool", "default": False},
|
|
651
|
+
"--label": {"key": "label", "type": "str", "default": None},
|
|
652
|
+
"--max-parallel": {"key": "max_parallel", "type": "str", "default": None, "transform": _parse_max_parallel},
|
|
602
653
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
603
654
|
}, RUN_USAGE, positionals_key="names", max_positionals=1)
|
|
604
655
|
if not parsed["json"]:
|
|
@@ -653,6 +704,8 @@ def _parse_run_args(args):
|
|
|
653
704
|
"timeout_seconds": parsed["timeout_seconds"],
|
|
654
705
|
"detach": parsed["detach"],
|
|
655
706
|
"failover": parsed["failover"],
|
|
707
|
+
"label": parsed["label"],
|
|
708
|
+
"max_parallel": parsed["max_parallel"],
|
|
656
709
|
"refresh": parsed["refresh"],
|
|
657
710
|
"reasoning_effort": effort.get("reasoning_effort"),
|
|
658
711
|
"power": effort.get("power"),
|
|
@@ -817,12 +870,16 @@ def _parse_stats_args(args, now=None):
|
|
|
817
870
|
"--since": {"key": "since", "type": "str", "default": None},
|
|
818
871
|
"--from": {"key": "from", "type": "str", "default": None},
|
|
819
872
|
"--to": {"key": "to", "type": "str", "default": None},
|
|
873
|
+
"--by": {"key": "by", "type": "str", "default": "session"},
|
|
820
874
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
821
875
|
}, STATS_USAGE, positionals_key="names", max_positionals=1)
|
|
876
|
+
if parsed["by"] not in STATS_GROUPS:
|
|
877
|
+
raise CdxError(STATS_USAGE)
|
|
822
878
|
period = _parse_history_period(parsed, now, STATS_USAGE)
|
|
823
879
|
return {
|
|
824
880
|
"name": parsed["names"][0] if parsed["names"] else None,
|
|
825
881
|
"period": period,
|
|
882
|
+
"by": parsed["by"],
|
|
826
883
|
"json": parsed["json"],
|
|
827
884
|
}
|
|
828
885
|
|
package/src/cli_helpers.py
CHANGED
|
@@ -249,13 +249,23 @@ def _bootstrap_claude_setup_token(session, ctx):
|
|
|
249
249
|
ctx["out"](
|
|
250
250
|
"Minting a long-lived Claude setup-token for this session.\n"
|
|
251
251
|
)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
252
|
+
try:
|
|
253
|
+
run_info = _run_interactive_provider_command(
|
|
254
|
+
session,
|
|
255
|
+
"setup-token",
|
|
256
|
+
spawn=ctx.get("spawn"),
|
|
257
|
+
env_override=ctx.get("env"),
|
|
258
|
+
signal_emitter=ctx.get("signal_emitter"),
|
|
259
|
+
)
|
|
260
|
+
except Exception as error:
|
|
261
|
+
# A failed or interrupted run can still have printed the token.
|
|
262
|
+
failed_path = (getattr(error, "run_info", None) or {}).get("transcript_path")
|
|
263
|
+
if failed_path:
|
|
264
|
+
try:
|
|
265
|
+
os.remove(failed_path)
|
|
266
|
+
except OSError:
|
|
267
|
+
pass
|
|
268
|
+
raise
|
|
259
269
|
transcript_path = run_info.get("transcript_path")
|
|
260
270
|
if not transcript_path or not os.path.isfile(transcript_path):
|
|
261
271
|
raise CdxError(
|
|
@@ -303,11 +313,15 @@ def _format_launch_config(session, use_color=False):
|
|
|
303
313
|
("logics", "Logics"),
|
|
304
314
|
("notify", "Agent alerts"),
|
|
305
315
|
("notify_preview", "Response preview"),
|
|
316
|
+
("scrub_env", "Scrub agent env"),
|
|
317
|
+
("shared_memory", "Shared memory"),
|
|
318
|
+
("retro", "Session retro"),
|
|
306
319
|
("model", "Model"),
|
|
307
320
|
("fallback_model", "Fallback"),
|
|
308
321
|
("budget", "Budget"),
|
|
309
322
|
("extra_args", "Extra args"),
|
|
310
323
|
("priority", "Priority"),
|
|
324
|
+
("policy", "Policy"),
|
|
311
325
|
]:
|
|
312
326
|
rows.append([
|
|
313
327
|
_dim(label, use_color),
|
|
@@ -329,14 +343,14 @@ def _format_launch_settings_hint(name="<name>"):
|
|
|
329
343
|
)
|
|
330
344
|
|
|
331
345
|
def _format_launch_setting_value(launch, key, use_color=False):
|
|
332
|
-
if key in ("fast", "rtk", "logics", "notify", "notify_preview"):
|
|
346
|
+
if key in ("fast", "rtk", "logics", "notify", "notify_preview", "scrub_env", "shared_memory", "retro"):
|
|
333
347
|
if launch.get(key) is True:
|
|
334
348
|
return _style("on", "32", use_color)
|
|
335
349
|
if launch.get(key) is False:
|
|
336
350
|
return _style("off", "2", use_color)
|
|
337
351
|
if key == "logics":
|
|
338
352
|
return _dim("auto", use_color)
|
|
339
|
-
if key in ("notify", "notify_preview"):
|
|
353
|
+
if key in ("notify", "notify_preview", "scrub_env", "shared_memory", "retro"):
|
|
340
354
|
return _style("off", "2", use_color)
|
|
341
355
|
return _dim("default", use_color)
|
|
342
356
|
value = launch.get(key)
|
package/src/codex_usage.py
CHANGED
|
@@ -247,7 +247,7 @@ def _request_codex_app_server(auth_home, method, params, failure_reason, timeout
|
|
|
247
247
|
stdout=subprocess.PIPE,
|
|
248
248
|
stderr=subprocess.DEVNULL,
|
|
249
249
|
env=env,
|
|
250
|
-
text=True,
|
|
250
|
+
text=True, encoding="utf-8", errors="replace",
|
|
251
251
|
bufsize=1,
|
|
252
252
|
)
|
|
253
253
|
thread = threading.Thread(target=_reader_thread, args=(process.stdout, output), daemon=True)
|
package/src/commands/launch.py
CHANGED
|
@@ -6,6 +6,7 @@ Split out of cli_commands.py. Moved verbatim; re-exported by cli_commands.
|
|
|
6
6
|
import os
|
|
7
7
|
import shlex
|
|
8
8
|
|
|
9
|
+
from .. import retro, shared_memory
|
|
9
10
|
from ..agent_notify import notifications_enabled, provision, supports_agent_alerts
|
|
10
11
|
from ..cli_args import CAN_RESUME_USAGE, HANDOFF_USAGE, RESUME_USAGE, _parse_json_flag
|
|
11
12
|
from ..cli_helpers import (
|
|
@@ -19,7 +20,7 @@ from ..cli_helpers import (
|
|
|
19
20
|
_write_update_notice,
|
|
20
21
|
)
|
|
21
22
|
from ..cli_render import _dim, _info, _success, _warn
|
|
22
|
-
from ..config import PROVIDER_CODEX
|
|
23
|
+
from ..config import PROVIDER_CLAUDE, PROVIDER_CODEX
|
|
23
24
|
from ..errors import CdxError
|
|
24
25
|
from ..handoff import HandoffSourceError, launch_prompt, load_entry, prepare_entry, resolve_source
|
|
25
26
|
from ..interactive_usage import extract_interactive_usage, transcript_predates_run, usage_delta
|
|
@@ -33,12 +34,24 @@ from ..provider_runtime import (
|
|
|
33
34
|
from ..run_command import PROMPT_STDIN, read_run_prompt
|
|
34
35
|
from ..run_usage import estimate_cost
|
|
35
36
|
|
|
36
|
-
LAUNCH_USAGE = "Usage: cdx <name> [--dir PATH] [--prompt TEXT|--prompt-file PATH] [--resume] [--json]"
|
|
37
|
+
LAUNCH_USAGE = "Usage: cdx <name> [--dir PATH] [--prompt TEXT|--prompt-file PATH|--task TEXT [--end-state TEXT]] [--resume] [--json]"
|
|
38
|
+
|
|
39
|
+
#: The first message of a session started with --task. The end state is what the
|
|
40
|
+
#: user's go-ahead covers: a bare "show me first" made sessions stop after a commit
|
|
41
|
+
#: and wait for a push they could have made.
|
|
42
|
+
TASK_PROMPT = """Task: {task}
|
|
43
|
+
|
|
44
|
+
{end_state}
|
|
45
|
+
|
|
46
|
+
Before starting, read this repository's instructions (AGENTS.md, CLAUDE.md and what they reference) and use its skills where one fits; they override general habits."""
|
|
47
|
+
END_STATE = """End state: {end_state}
|
|
48
|
+
Reaching it is approved: take every step it needs, including the commits, pushes or pull requests it names, without asking again. Stop and ask only for a decision it does not cover."""
|
|
49
|
+
NO_END_STATE = """No end state was given: stop and show the result before any commit, push or pull request."""
|
|
37
50
|
|
|
38
51
|
|
|
39
52
|
def _parse_launch_args(args):
|
|
40
53
|
"""Return the per-launch directory, initial prompt source and ordinary launch flags."""
|
|
41
|
-
values = {"--dir": None, "--prompt": None, "--prompt-file": None}
|
|
54
|
+
values = {"--dir": None, "--prompt": None, "--prompt-file": None, "--task": None, "--end-state": None}
|
|
42
55
|
cleaned = []
|
|
43
56
|
index = 0
|
|
44
57
|
while index < len(args):
|
|
@@ -59,6 +72,13 @@ def _parse_launch_args(args):
|
|
|
59
72
|
values[flag] = value
|
|
60
73
|
if values["--prompt"] is not None and values["--prompt-file"] is not None:
|
|
61
74
|
raise CdxError("cdx <name>: --prompt and --prompt-file are mutually exclusive.")
|
|
75
|
+
if values["--end-state"] is not None and values["--task"] is None:
|
|
76
|
+
raise CdxError("cdx <name>: --end-state goes with --task.")
|
|
77
|
+
if values["--task"] is not None:
|
|
78
|
+
if values["--prompt"] is not None or values["--prompt-file"] is not None:
|
|
79
|
+
raise CdxError("cdx <name>: --task replaces --prompt and --prompt-file.")
|
|
80
|
+
end = END_STATE.format(end_state=values["--end-state"]) if values["--end-state"] else NO_END_STATE
|
|
81
|
+
values["--prompt"] = TASK_PROMPT.format(task=values["--task"], end_state=end)
|
|
62
82
|
if values["--prompt-file"] == PROMPT_STDIN:
|
|
63
83
|
raise CdxError("cdx <name>: --prompt-file - is not supported; the interactive session needs standard input.")
|
|
64
84
|
prompt = {"prompt": values["--prompt"], "prompt_file": values["--prompt-file"]}
|
|
@@ -202,6 +222,21 @@ def _notification_provisioning_enabled(session, ctx, json_flag):
|
|
|
202
222
|
return True
|
|
203
223
|
|
|
204
224
|
|
|
225
|
+
def _link_shared_memory(session, ctx, cwd, json_flag):
|
|
226
|
+
if session["provider"] != PROVIDER_CLAUDE or (session.get("launch") or {}).get("shared_memory") is not True:
|
|
227
|
+
return
|
|
228
|
+
try:
|
|
229
|
+
status, detail = shared_memory.link(_get_auth_home(session), ctx["service"]["base_dir"], cwd)
|
|
230
|
+
except OSError as error:
|
|
231
|
+
status, detail = "unsupported", str(error)
|
|
232
|
+
if json_flag or status == "linked":
|
|
233
|
+
return
|
|
234
|
+
if status == "conflict":
|
|
235
|
+
detail = f"merge these files by hand first: {', '.join(detail)}"
|
|
236
|
+
message = f"Shared memory not linked for {session['name']}"
|
|
237
|
+
ctx["out"](f"{_warn(message, ctx['use_color'])} {detail}\n")
|
|
238
|
+
|
|
239
|
+
|
|
205
240
|
def handle_launch(command, ctx, initial_prompt=None, resume=False, force_json=None, directory=None):
|
|
206
241
|
json_flag = "--json" in ctx.get("raw_args", ctx["options"].get("raw_args", []))
|
|
207
242
|
if force_json is not None:
|
|
@@ -262,6 +297,7 @@ def handle_launch(command, ctx, initial_prompt=None, resume=False, force_json=No
|
|
|
262
297
|
# a real turn — so it gets no instruction it does not need.
|
|
263
298
|
notice += " — approve them once in Codex to enable"
|
|
264
299
|
ctx["out"](f"{_info(notice, ctx['use_color'])}\n")
|
|
300
|
+
_link_shared_memory(session, ctx, cwd, json_flag)
|
|
265
301
|
runtime_run_id = None
|
|
266
302
|
|
|
267
303
|
def runtime_lifecycle(event, info):
|
|
@@ -335,6 +371,8 @@ def handle_launch(command, ctx, initial_prompt=None, resume=False, force_json=No
|
|
|
335
371
|
})
|
|
336
372
|
if not json_flag:
|
|
337
373
|
ctx["out"](f"{_goodbye_line(session, success_run_info, ctx['use_color'])}\n")
|
|
374
|
+
if retro.start(session, cwd, success_run_info.get("provider_transcript_path"), env=ctx.get("env")) and not json_flag:
|
|
375
|
+
ctx["out"](f"{_dim('Session retro started; read it later with `cdx retro`', ctx['use_color'])}\n")
|
|
338
376
|
if json_flag:
|
|
339
377
|
extra = {"session": ctx["service"]["get_session"](session["name"]), "cwd": cwd}
|
|
340
378
|
if capability:
|
|
@@ -775,6 +775,7 @@ def handle_update(rest, ctx):
|
|
|
775
775
|
package_root=ctx["options"].get("packageRoot"),
|
|
776
776
|
prefix=ctx["options"].get("prefix"),
|
|
777
777
|
base_prefix=ctx["options"].get("basePrefix"),
|
|
778
|
+
env=ctx.get("env"),
|
|
778
779
|
)
|
|
779
780
|
results = run_update_plan(plan, runner=ctx["options"].get("runUpdate"), env=ctx.get("env"))
|
|
780
781
|
failed = any((result.get("returncode") != 0) for result in results)
|