hermes-task-framework 1.4.0__tar.gz → 1.5.0__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.
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/PKG-INFO +1 -1
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-cross-machine-sync/SKILL.md +52 -1
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/manage_task.py +69 -3
- hermes_task_framework-1.5.0/hermes_task_framework/tests/test_task_model_contract.py +71 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/PKG-INFO +1 -1
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/SOURCES.txt +1 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/pyproject.toml +1 -1
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/LICENSE +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/README.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/__init__.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/compact-directory-memory/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/compact-directory-memory/scripts/manage_directory_context.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-archaeology/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-artifact-integrity/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-artifact-integrity/references/task-root-relocation.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-artifact-integrity/scripts/task_integrity.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-aware-project-work/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-context-storage/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-cross-machine-sync/references/on-demand-interactive-task-windows.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-cross-machine-sync/scripts/task_checkpoint_sync.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-cross-machine-sync/scripts/task_git_sync.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-external-repos-pattern/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/CHANGELOG.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/SUBTASKS.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/TASKS.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/architecture.dot +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/architecture.svg +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/product-requirements.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/auto-runner.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/composites/code-review-session.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/composites/research.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/composites/software-dev.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/document-analysis-workflow.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/file-safety-lesson.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/generating-output-documents.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/code-write.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/document-write.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/info-search.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/web-research.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/policy-time-metadata.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/research-workflow.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-format-validation.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-hash-naming.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-lifecycle-example.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-overview-discovery.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-types/analysis.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-types/external-audit.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-types/video-production-pipeline.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/__init__.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/convert_md_to_pdf.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task-runner.sh +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task_api.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task_ref.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task_symlink_policy.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task_write_lock.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/todo_lifecycle.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/update-index.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/CHANGELOG.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/MEMORY.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/TASK.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/run.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-discipline/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-edge-cases/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-edge-cases/references/task-5d5a1a-recovery-example.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-portability/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-portability/references/output-model-design.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-nested-subtask-lifecycle/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-requirement-intake/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-timestamp-convention/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-tracker/SKILL.md +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_directory_context.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_nested_subtasks.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_skill_ownership.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_api.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_checkpoint_sync.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_git_sync.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_integrity.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_root_resolution.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_symlink_policy.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_todo_lifecycle.py +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/dependency_links.txt +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/top_level.txt +0 -0
- {hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hermes-task-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Structured task management skills for Hermes Agent — task creation, decomposition, stage gates
|
|
5
5
|
Project-URL: Source, https://github.com/nuffin/hermes-task-framework-pip
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -16,7 +16,7 @@ metadata:
|
|
|
16
16
|
properties: {reason: validates context after transport, strength: strong}
|
|
17
17
|
name: task-cross-machine-sync
|
|
18
18
|
platforms: [linux, macos]
|
|
19
|
-
version: 2.
|
|
19
|
+
version: 2.1.0
|
|
20
20
|
---
|
|
21
21
|
|
|
22
22
|
# Task Cross-Machine Sync
|
|
@@ -30,6 +30,57 @@ Synchronizes the configured `$HERMES_TASKS_ROOT` as a Git repository. It contain
|
|
|
30
30
|
- optional machine role and conflict policy supplied outside this skill;
|
|
31
31
|
- optional Git LFS policy for large artifacts.
|
|
32
32
|
|
|
33
|
+
## Remote execution model contract
|
|
34
|
+
|
|
35
|
+
Every task that may be explicitly dispatched to another machine has one
|
|
36
|
+
immutable-at-dispatch provider/model pair. Store it in canonical task metadata:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"extensions": {
|
|
41
|
+
"remote_execution": {
|
|
42
|
+
"provider": "openai-codex",
|
|
43
|
+
"model": "gpt-5.6-terra-900k",
|
|
44
|
+
"provider_source": "explicit",
|
|
45
|
+
"model_source": "explicit"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Creation-time capture
|
|
52
|
+
|
|
53
|
+
The task creation API resolves both fields once, independently but with the same
|
|
54
|
+
source order:
|
|
55
|
+
|
|
56
|
+
1. explicit `manage_task.py create --model <model> --provider <provider>`;
|
|
57
|
+
2. Hermes launch-scoped `HERMES_MODEL` / `HERMES_INFERENCE_MODEL` and
|
|
58
|
+
`HERMES_TUI_PROVIDER` / `HERMES_INFERENCE_PROVIDER`;
|
|
59
|
+
3. current profile configuration, then global configuration, `model.default`
|
|
60
|
+
and `model.provider`.
|
|
61
|
+
|
|
62
|
+
The creating conversation must pass both `--model` and `--provider` when it has
|
|
63
|
+
a temporary per-conversation override that is not exported into the process
|
|
64
|
+
environment. Do not defer this decision until remote dispatch: the controller
|
|
65
|
+
may later use a different provider/model pair.
|
|
66
|
+
|
|
67
|
+
### Dispatch-time resolution
|
|
68
|
+
|
|
69
|
+
An explicit dispatch provider/model pair may override the stored creation pair
|
|
70
|
+
only when the controller records both values in the dispatch request. Otherwise
|
|
71
|
+
dispatch uses `extensions.remote_execution.provider` and `.model`. The receipt,
|
|
72
|
+
`LaunchSpec`, and result manifest must all record the same resolved pair. A
|
|
73
|
+
missing provider or model is a blocked dispatch; never silently fall back to the
|
|
74
|
+
executor's local provider or model defaults.
|
|
75
|
+
|
|
76
|
+
The task-specific runtime overlay starts Hermes with its receipt-derived pair:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
hermes -p <executor-profile> -m <resolved-model> --provider <resolved-provider>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Generic SSH/tmux runtime remains model-agnostic and never reads task metadata.
|
|
83
|
+
|
|
33
84
|
## Preconditions
|
|
34
85
|
|
|
35
86
|
1. Confirm the exact task root and Git repository status.
|
|
@@ -44,6 +44,58 @@ def _read_config_yaml(config_path):
|
|
|
44
44
|
return {}
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
MODEL_IDENTIFIER_PATTERN = re.compile(r"^[^\s\x00]{1,256}$")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _model_from_config(config):
|
|
51
|
+
value = config.get("model") if isinstance(config, dict) else None
|
|
52
|
+
if isinstance(value, dict):
|
|
53
|
+
value = value.get("default")
|
|
54
|
+
return value.strip() if isinstance(value, str) and value.strip() else None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _provider_from_config(config):
|
|
58
|
+
value = config.get("model") if isinstance(config, dict) else None
|
|
59
|
+
if isinstance(value, dict):
|
|
60
|
+
value = value.get("provider")
|
|
61
|
+
return value.strip() if isinstance(value, str) and value.strip() else None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _resolve_value(candidates, field_name):
|
|
65
|
+
for value, source in candidates:
|
|
66
|
+
if value is None:
|
|
67
|
+
continue
|
|
68
|
+
value = str(value).strip()
|
|
69
|
+
if not value:
|
|
70
|
+
continue
|
|
71
|
+
if not MODEL_IDENTIFIER_PATTERN.fullmatch(value):
|
|
72
|
+
raise ValueError(f"{field_name} must be non-empty text without whitespace or NUL bytes")
|
|
73
|
+
return value, source
|
|
74
|
+
return None, None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _resolve_creation_model(explicit_model=None, explicit_provider=None):
|
|
78
|
+
"""Capture the provider/model pair at creation; never infer it at dispatch."""
|
|
79
|
+
hermes_home = os.environ.get("HERMES_HOME", "").strip()
|
|
80
|
+
profile_config = _read_config_yaml(Path(hermes_home) / "config.yaml") if hermes_home else {}
|
|
81
|
+
global_config = _read_config_yaml(Path.home() / ".hermes" / "config.yaml")
|
|
82
|
+
model, model_source = _resolve_value([
|
|
83
|
+
(explicit_model, "explicit"),
|
|
84
|
+
(os.environ.get("HERMES_MODEL"), "launch_env"),
|
|
85
|
+
(os.environ.get("HERMES_INFERENCE_MODEL"), "launch_env"),
|
|
86
|
+
(_model_from_config(profile_config), "profile_config"),
|
|
87
|
+
(_model_from_config(global_config), "global_config"),
|
|
88
|
+
], "model")
|
|
89
|
+
provider, provider_source = _resolve_value([
|
|
90
|
+
(explicit_provider, "explicit"),
|
|
91
|
+
(os.environ.get("HERMES_TUI_PROVIDER"), "launch_env"),
|
|
92
|
+
(os.environ.get("HERMES_INFERENCE_PROVIDER"), "launch_env"),
|
|
93
|
+
(_provider_from_config(profile_config), "profile_config"),
|
|
94
|
+
(_provider_from_config(global_config), "global_config"),
|
|
95
|
+
], "provider")
|
|
96
|
+
return model, model_source, provider, provider_source
|
|
97
|
+
|
|
98
|
+
|
|
47
99
|
def _resolve_tasks_root() -> str:
|
|
48
100
|
"""Resolve the task root with ROOT as canonical and DIR as legacy fallback."""
|
|
49
101
|
# 1. Canonical env var, then the legacy pip-package compatibility alias.
|
|
@@ -705,7 +757,7 @@ def _exact_name_matches(slug):
|
|
|
705
757
|
return matches
|
|
706
758
|
|
|
707
759
|
|
|
708
|
-
def cmd_create(name, from_inbox=None, description=None, allow_duplicate=False, parent=None):
|
|
760
|
+
def cmd_create(name, from_inbox=None, description=None, allow_duplicate=False, parent=None, model=None, provider=None):
|
|
709
761
|
"""Create a new task: directory + hash + meta + templates + (optional inbox file/dir move)."""
|
|
710
762
|
slug = re.sub(r'[^a-z0-9-]', '', name.lower().replace(' ', '-')).strip('-')
|
|
711
763
|
if not slug:
|
|
@@ -747,6 +799,16 @@ def cmd_create(name, from_inbox=None, description=None, allow_duplicate=False, p
|
|
|
747
799
|
"required_by": [],
|
|
748
800
|
"priority": 2,
|
|
749
801
|
}
|
|
802
|
+
creation_model, model_source, creation_provider, provider_source = _resolve_creation_model(model, provider)
|
|
803
|
+
if creation_model or creation_provider:
|
|
804
|
+
meta["extensions"] = {
|
|
805
|
+
"remote_execution": {
|
|
806
|
+
"model": creation_model,
|
|
807
|
+
"model_source": model_source,
|
|
808
|
+
"provider": creation_provider,
|
|
809
|
+
"provider_source": provider_source,
|
|
810
|
+
}
|
|
811
|
+
}
|
|
750
812
|
if parent_dir:
|
|
751
813
|
parent_meta_path = os.path.join(parent_dir, ".hermes-task.json")
|
|
752
814
|
parent_meta = json.loads(open(parent_meta_path, encoding="utf-8").read()) if os.path.exists(parent_meta_path) else {}
|
|
@@ -955,7 +1017,7 @@ def main():
|
|
|
955
1017
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
956
1018
|
epilog="""\
|
|
957
1019
|
new commands:
|
|
958
|
-
create <name> [--from-inbox <file-or-dir>] [--desc <text>]
|
|
1020
|
+
create <name> [--from-inbox <file-or-dir>] [--desc <text>] [--model <model>] [--provider <provider>]
|
|
959
1021
|
accept <inbox_item> [--name <task-name>] Accept inbox item into a new task
|
|
960
1022
|
decline <inbox_item> [--reason <text>] Move inbox item to declined/
|
|
961
1023
|
status <hash_or_dir> <status> [--reason <text>] Update task status
|
|
@@ -976,6 +1038,10 @@ new commands:
|
|
|
976
1038
|
help='(create) bypass exact-name duplicate prevention')
|
|
977
1039
|
parser.add_argument('--parent', dest='parent', default=None,
|
|
978
1040
|
help='(create) create as a contained subtask of an existing task')
|
|
1041
|
+
parser.add_argument('--model', dest='model', default=None,
|
|
1042
|
+
help='(create) persist the task creation model for remote dispatch')
|
|
1043
|
+
parser.add_argument('--provider', dest='provider', default=None,
|
|
1044
|
+
help='(create) persist the task creation provider for remote dispatch')
|
|
979
1045
|
parser.add_argument('--name', dest='task_name', default=None,
|
|
980
1046
|
help='(accept) override auto-derived task name')
|
|
981
1047
|
parser.add_argument('--reason', dest='reason', default='',
|
|
@@ -993,7 +1059,7 @@ new commands:
|
|
|
993
1059
|
'list': lambda: cmd_list(args.args[0] if args.args else None),
|
|
994
1060
|
|
|
995
1061
|
'ensure-all': cmd_ensure_all,
|
|
996
|
-
'create': lambda: cmd_create(args.args[0], from_inbox=args.from_inbox, description=args.description, allow_duplicate=args.allow_duplicate, parent=args.parent) if args.args else False,
|
|
1062
|
+
'create': lambda: cmd_create(args.args[0], from_inbox=args.from_inbox, description=args.description, allow_duplicate=args.allow_duplicate, parent=args.parent, model=args.model, provider=args.provider) if args.args else False,
|
|
997
1063
|
'accept': lambda: cmd_accept(args.args[0], name=args.task_name) if args.args else False,
|
|
998
1064
|
'decline': lambda: cmd_decline(args.args[0], reason=args.reason) if args.args else False,
|
|
999
1065
|
'status': lambda: cmd_status(args.args[0], args.args[1], reason=args.reason) if len(args.args) >= 2 else False,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import tempfile
|
|
6
|
+
import unittest
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
11
|
+
MANAGE = ROOT / "skills" / "task-framework" / "scripts" / "manage_task.py"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TaskModelContractTests(unittest.TestCase):
|
|
15
|
+
def create_task(self, env, *extra):
|
|
16
|
+
subprocess.run(
|
|
17
|
+
[sys.executable, str(MANAGE), "create", "model-contract", *extra],
|
|
18
|
+
env=env, check=True, capture_output=True, text=True,
|
|
19
|
+
)
|
|
20
|
+
task_dir = next(Path(env["HERMES_TASKS_ROOT"]).glob("*.model-contract-*"))
|
|
21
|
+
return json.loads((task_dir / ".hermes-task.json").read_text(encoding="utf-8"))
|
|
22
|
+
|
|
23
|
+
def test_explicit_model_is_persisted_and_wins_over_launch_environment(self):
|
|
24
|
+
with tempfile.TemporaryDirectory() as temporary:
|
|
25
|
+
env = os.environ.copy()
|
|
26
|
+
env["HERMES_TASKS_ROOT"] = temporary
|
|
27
|
+
env["HERMES_INFERENCE_MODEL"] = "openai/env-model"
|
|
28
|
+
metadata = self.create_task(env, "--model", "openai/explicit-model", "--provider", "openai-codex")
|
|
29
|
+
execution = metadata["extensions"]["remote_execution"]
|
|
30
|
+
self.assertEqual(execution, {
|
|
31
|
+
"model": "openai/explicit-model", "model_source": "explicit",
|
|
32
|
+
"provider": "openai-codex", "provider_source": "explicit",
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
def test_launch_model_is_captured_when_no_explicit_model_is_given(self):
|
|
36
|
+
with tempfile.TemporaryDirectory() as temporary:
|
|
37
|
+
env = os.environ.copy()
|
|
38
|
+
env["HERMES_TASKS_ROOT"] = temporary
|
|
39
|
+
env["HERMES_INFERENCE_MODEL"] = "openai/launch-model"
|
|
40
|
+
env["HERMES_INFERENCE_PROVIDER"] = "openai-codex"
|
|
41
|
+
metadata = self.create_task(env)
|
|
42
|
+
execution = metadata["extensions"]["remote_execution"]
|
|
43
|
+
self.assertEqual(execution, {
|
|
44
|
+
"model": "openai/launch-model", "model_source": "launch_env",
|
|
45
|
+
"provider": "openai-codex", "provider_source": "launch_env",
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
def test_profile_config_default_is_captured_when_no_launch_model_exists(self):
|
|
49
|
+
with tempfile.TemporaryDirectory() as temporary:
|
|
50
|
+
profile = Path(temporary) / "profile"
|
|
51
|
+
profile.mkdir()
|
|
52
|
+
(profile / "config.yaml").write_text(
|
|
53
|
+
"model:\n default: openai/config-model\n provider: openai-codex\n", encoding="utf-8"
|
|
54
|
+
)
|
|
55
|
+
env = os.environ.copy()
|
|
56
|
+
env["HERMES_TASKS_ROOT"] = str(Path(temporary) / "tasks")
|
|
57
|
+
env["HERMES_HOME"] = str(profile)
|
|
58
|
+
env.pop("HERMES_MODEL", None)
|
|
59
|
+
env.pop("HERMES_INFERENCE_MODEL", None)
|
|
60
|
+
env.pop("HERMES_TUI_PROVIDER", None)
|
|
61
|
+
env.pop("HERMES_INFERENCE_PROVIDER", None)
|
|
62
|
+
metadata = self.create_task(env)
|
|
63
|
+
execution = metadata["extensions"]["remote_execution"]
|
|
64
|
+
self.assertEqual(execution, {
|
|
65
|
+
"model": "openai/config-model", "model_source": "profile_config",
|
|
66
|
+
"provider": "openai-codex", "provider_source": "profile_config",
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
unittest.main()
|
{hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hermes-task-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Structured task management skills for Hermes Agent — task creation, decomposition, stage gates
|
|
5
5
|
Project-URL: Source, https://github.com/nuffin/hermes-task-framework-pip
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -76,6 +76,7 @@ hermes_task_framework/tests/test_task_api.py
|
|
|
76
76
|
hermes_task_framework/tests/test_task_checkpoint_sync.py
|
|
77
77
|
hermes_task_framework/tests/test_task_git_sync.py
|
|
78
78
|
hermes_task_framework/tests/test_task_integrity.py
|
|
79
|
+
hermes_task_framework/tests/test_task_model_contract.py
|
|
79
80
|
hermes_task_framework/tests/test_task_root_resolution.py
|
|
80
81
|
hermes_task_framework/tests/test_task_symlink_policy.py
|
|
81
82
|
hermes_task_framework/tests/test_todo_lifecycle.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hermes-task-framework"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.5.0"
|
|
8
8
|
description = "Structured task management skills for Hermes Agent — task creation, decomposition, stage gates"
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
dependencies = []
|
|
File without changes
|
|
File without changes
|
{hermes_task_framework-1.4.0 → hermes_task_framework-1.5.0}/hermes_task_framework/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|