@litfamily/lithermes 1.0.13 → 1.0.15
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 +11 -27
- package/README_Ko-KR.md +11 -27
- package/assets/lithermes-plugin/README.md +35 -4
- package/assets/lithermes-plugin/__init__.py +25 -2
- package/assets/lithermes-plugin/auto_handoff.py +733 -0
- package/assets/lithermes-plugin/core.py +10 -0
- package/assets/lithermes-plugin/diagnostics.py +12 -2
- package/assets/lithermes-plugin/handoff.py +13 -2
- package/assets/lithermes-plugin/payload-version.json +15 -11
- package/assets/lithermes-plugin/plugin.yaml +1 -1
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +2 -2
- package/assets/lithermes-plugin/skills/lit-handoff/SKILL.md +7 -0
- package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +2 -2
- package/package.json +1 -1
- package/readme-assets/badge-version.svg +1 -1
- package/src/lib/modelRoutePolicy.js +9 -1
- package/readme-assets/ab-simple/s11-ui-baseline-desktop.webp +0 -0
- package/readme-assets/ab-simple/s11-ui-lithermes-desktop.webp +0 -0
- package/readme-assets/ab-simple/s3-ui-baseline-desktop.webp +0 -0
- package/readme-assets/ab-simple/s3-ui-lithermes-desktop.webp +0 -0
- package/readme-assets/ab-simple/s4-ui-baseline-desktop.webp +0 -0
- package/readme-assets/ab-simple/s4-ui-lithermes-desktop.webp +0 -0
- package/readme-assets/ab-simple/s5-office-baseline-slides.webp +0 -0
- package/readme-assets/ab-simple/s5-office-lithermes-slides.webp +0 -0
- package/readme-assets/ab-simple/s7-lithermes-diagram.webp +0 -0
- package/readme-assets/ab-simple/s8-office-baseline-slides.webp +0 -0
- package/readme-assets/ab-simple/s8-office-lithermes-slides.webp +0 -0
- package/readme-assets/ab-simple/s9-office-lithermes-pages.webp +0 -0
|
@@ -76,6 +76,11 @@ try:
|
|
|
76
76
|
except (ImportError, ModuleNotFoundError):
|
|
77
77
|
import auto_update as _auto_update
|
|
78
78
|
|
|
79
|
+
try:
|
|
80
|
+
from . import auto_handoff as _auto_handoff
|
|
81
|
+
except (ImportError, ModuleNotFoundError):
|
|
82
|
+
import auto_handoff as _auto_handoff
|
|
83
|
+
|
|
79
84
|
try:
|
|
80
85
|
from . import output_styles as _output_styles
|
|
81
86
|
except (ImportError, ModuleNotFoundError):
|
|
@@ -155,6 +160,10 @@ def post_api_request(**kwargs: Any) -> None:
|
|
|
155
160
|
fields, but the measurement module intentionally ignores them. Returning
|
|
156
161
|
``None`` preserves the observer-only hook contract.
|
|
157
162
|
"""
|
|
163
|
+
try:
|
|
164
|
+
_auto_handoff.post_api_request(**kwargs)
|
|
165
|
+
except Exception: # noqa: BLE001 - the optional handoff reading never costs a model call
|
|
166
|
+
pass
|
|
158
167
|
return _provider_metrics.post_api_request(**kwargs)
|
|
159
168
|
|
|
160
169
|
|
|
@@ -191,6 +200,7 @@ def on_session_start(**kwargs: Any) -> None:
|
|
|
191
200
|
release_browser_drive_state(kwargs.get("session_id"))
|
|
192
201
|
_deliverable_hedges.release_session(kwargs.get("session_id"))
|
|
193
202
|
_rules.begin_session(str(kwargs.get("session_id") or ""))
|
|
203
|
+
_auto_handoff.begin_session(kwargs.get("session_id"))
|
|
194
204
|
_print_agents_nudge(str(kwargs.get("platform") or ""))
|
|
195
205
|
return None
|
|
196
206
|
|
|
@@ -26,6 +26,11 @@ try:
|
|
|
26
26
|
except (ImportError, ModuleNotFoundError):
|
|
27
27
|
import jev_hint as _jev_hint # type: ignore
|
|
28
28
|
|
|
29
|
+
try:
|
|
30
|
+
from . import auto_handoff as _auto_handoff
|
|
31
|
+
except (ImportError, ModuleNotFoundError):
|
|
32
|
+
import auto_handoff as _auto_handoff # type: ignore
|
|
33
|
+
|
|
29
34
|
try:
|
|
30
35
|
from .core_contract import KOREAN_PROSE_ALIASES, KOREAN_PROSE_COMMANDS
|
|
31
36
|
except (ImportError, ModuleNotFoundError):
|
|
@@ -74,7 +79,8 @@ _RUNTIME_STEM = "".join(("co", "dex"))
|
|
|
74
79
|
_OPENAI_PROVIDER = f"openai-{_RUNTIME_STEM}"
|
|
75
80
|
_RESPONSES_MODE = f"{_RUNTIME_STEM}_responses"
|
|
76
81
|
_ASTRA_MODEL = "gpt-6-astra"
|
|
77
|
-
_SOL_MODEL = "gpt-6-sol"
|
|
82
|
+
_SOL_MODEL = "gpt-6.1-sol"
|
|
83
|
+
_PREVIOUS_SOL_MODEL = "gpt-6-sol"
|
|
78
84
|
_LUNA_MODEL = "gpt-6-luna"
|
|
79
85
|
_LEGACY_SOL_MODEL = "gpt-5.6-sol"
|
|
80
86
|
_TERRA_MODEL = "gpt-5.6-terra"
|
|
@@ -89,6 +95,7 @@ _ASTRA_EFFORTS = _GPT6_LEAD_EFFORTS
|
|
|
89
95
|
_OPENAI_CATALOG_EFFORTS = {
|
|
90
96
|
_ASTRA_MODEL: _ASTRA_EFFORTS,
|
|
91
97
|
_SOL_MODEL: _GPT6_LEAD_EFFORTS,
|
|
98
|
+
_PREVIOUS_SOL_MODEL: _GPT6_LEAD_EFFORTS,
|
|
92
99
|
_LUNA_MODEL: _GPT6_LUNA_EFFORTS,
|
|
93
100
|
_LEGACY_SOL_MODEL: _LEGACY_SOL_EFFORTS,
|
|
94
101
|
_TERRA_MODEL: _TERRA_EFFORTS,
|
|
@@ -352,7 +359,7 @@ def _parse_capabilities(host_version: str) -> CapabilityResult:
|
|
|
352
359
|
astra = (astra_parent or astra_child) and cataloged_parent and cataloged_child
|
|
353
360
|
sol_lead = (
|
|
354
361
|
provider == _OPENAI_PROVIDER
|
|
355
|
-
and model in (_SOL_MODEL, _LEGACY_SOL_MODEL)
|
|
362
|
+
and model in (_SOL_MODEL, _PREVIOUS_SOL_MODEL, _LEGACY_SOL_MODEL)
|
|
356
363
|
and _cataloged_route(model, effort)
|
|
357
364
|
and child_model in _LUNA_MODELS
|
|
358
365
|
and _cataloged_route(child_model, child_effort)
|
|
@@ -483,6 +490,7 @@ def status_report(host_version: str | None = None) -> str:
|
|
|
483
490
|
_knowledge.status_line(Path.cwd()),
|
|
484
491
|
_provider_metrics.capability_line(),
|
|
485
492
|
_jev_hint.status_line(),
|
|
493
|
+
_auto_handoff.status_line(),
|
|
486
494
|
f"litgoal evidence kinds: {', '.join(_evidence_kinds())}",
|
|
487
495
|
f"scientific visualization: {_science.dependency_summary()}",
|
|
488
496
|
*_motion_runtime_lines(),
|
|
@@ -571,6 +579,8 @@ def doctor_report(host_version: str | None = None) -> tuple[list[str], int]:
|
|
|
571
579
|
jev_state = _jev_hint.status()
|
|
572
580
|
jev_tag = {"on": "OK", "off": "NOTE"}.get(jev_state, "WARN")
|
|
573
581
|
lines.append(f"[{jev_tag}] {_jev_hint.status_line()}")
|
|
582
|
+
handoff_tag, handoff_text = _auto_handoff.doctor_line()
|
|
583
|
+
lines.append(f"[{handoff_tag}] {handoff_text}")
|
|
574
584
|
if version != "unknown":
|
|
575
585
|
lines.append(f"[OK] Hermes host detected (v{version})")
|
|
576
586
|
else:
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import html
|
|
4
|
+
import os
|
|
4
5
|
import re
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
9
|
try:
|
|
10
|
+
from . import auto_handoff
|
|
9
11
|
from .core_contract import (
|
|
10
12
|
reader_facing_contract_block,
|
|
11
13
|
reader_facing_mode_from_user_request,
|
|
@@ -14,6 +16,7 @@ try:
|
|
|
14
16
|
from .redaction import redact_text
|
|
15
17
|
from .session_context import is_delegate_child_platform
|
|
16
18
|
except (ImportError, ModuleNotFoundError):
|
|
19
|
+
import auto_handoff # type: ignore
|
|
17
20
|
from core_contract import ( # type: ignore
|
|
18
21
|
reader_facing_contract_block,
|
|
19
22
|
reader_facing_mode_from_user_request,
|
|
@@ -65,8 +68,16 @@ def _agent_message(raw_args: str) -> str:
|
|
|
65
68
|
)
|
|
66
69
|
|
|
67
70
|
|
|
68
|
-
def command_lit_handoff(raw_args: str) -> dict[str, str]:
|
|
69
|
-
"""Build
|
|
71
|
+
def command_lit_handoff(raw_args: str) -> dict[str, str] | str:
|
|
72
|
+
"""Build model context for the native slash command.
|
|
73
|
+
|
|
74
|
+
``auto on <percent> | off | status`` is the user's switch for the automatic
|
|
75
|
+
handoff; it answers in plain text and sends nothing to the model. Every
|
|
76
|
+
other argument is a focus for a normal handoff and stays side-effect-free.
|
|
77
|
+
"""
|
|
78
|
+
switch = auto_handoff.parse_route(raw_args)
|
|
79
|
+
if switch is not None:
|
|
80
|
+
return auto_handoff.run_command(switch, session_id=os.environ.get(auto_handoff.SESSION_ENV, "").strip())
|
|
70
81
|
return {
|
|
71
82
|
"display": f"{acknowledgement('lit-handoff', color=True)}\nHandoff source loaded; inspect live state before writing.",
|
|
72
83
|
"agent_message": _agent_message(raw_args),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"syncedAt": "2026-09-
|
|
2
|
+
"syncedAt": "2026-09-30T13:06:01.009Z",
|
|
3
3
|
"source": "bundled-payload",
|
|
4
|
-
"sourceHash": "
|
|
4
|
+
"sourceHash": "92b298c9a38364d94b56fb497641d44835bb3bda3230338eb6a769647a5f61b5",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": "NOTICE.md",
|
|
@@ -9,11 +9,15 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "README.md",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "234ed5a80d74dec9953f5168b85f666de656b3511c23a74e26194748a4f3b766"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "__init__.py",
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "76cf72655600b82544e9047fab1f5f4030927803c0cfbc08fca311204f37d32c"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "auto_handoff.py",
|
|
20
|
+
"sha256": "7c0e5385373cc83b202669a9ab500d46e04d79c5a188ed2ffdea93f863ec7dab"
|
|
17
21
|
},
|
|
18
22
|
{
|
|
19
23
|
"path": "auto_update.py",
|
|
@@ -29,7 +33,7 @@
|
|
|
29
33
|
},
|
|
30
34
|
{
|
|
31
35
|
"path": "core.py",
|
|
32
|
-
"sha256": "
|
|
36
|
+
"sha256": "32b7ef2c93e3f208e39e26a6eca4a03f48804b7ddf6b7a16eaf4cf038fbf4e21"
|
|
33
37
|
},
|
|
34
38
|
{
|
|
35
39
|
"path": "core_commands.py",
|
|
@@ -85,11 +89,11 @@
|
|
|
85
89
|
},
|
|
86
90
|
{
|
|
87
91
|
"path": "diagnostics.py",
|
|
88
|
-
"sha256": "
|
|
92
|
+
"sha256": "bd797eadf291ec9cd6c02926595305532e235e21794e4467e157c03b0311fe54"
|
|
89
93
|
},
|
|
90
94
|
{
|
|
91
95
|
"path": "handoff.py",
|
|
92
|
-
"sha256": "
|
|
96
|
+
"sha256": "f34788ee4b56b6156e6503b546ea416fd886856df2c8f9e60006eb2b0289a027"
|
|
93
97
|
},
|
|
94
98
|
{
|
|
95
99
|
"path": "humanizer_detector.py",
|
|
@@ -177,7 +181,7 @@
|
|
|
177
181
|
},
|
|
178
182
|
{
|
|
179
183
|
"path": "plugin.yaml",
|
|
180
|
-
"sha256": "
|
|
184
|
+
"sha256": "b086300c1f74bf85aca8be93bf6fdf9112e35536f050ff109456584952c1ec78"
|
|
181
185
|
},
|
|
182
186
|
{
|
|
183
187
|
"path": "provider_metrics.py",
|
|
@@ -1281,7 +1285,7 @@
|
|
|
1281
1285
|
},
|
|
1282
1286
|
{
|
|
1283
1287
|
"path": "skills/frontend-ui-ux/references/complete-contract.md",
|
|
1284
|
-
"sha256": "
|
|
1288
|
+
"sha256": "a42439ee181e68b729f969e5b8243205566a64e6f212894f2284735f2737809c"
|
|
1285
1289
|
},
|
|
1286
1290
|
{
|
|
1287
1291
|
"path": "skills/frontend-ui-ux/references/composition.md",
|
|
@@ -3157,7 +3161,7 @@
|
|
|
3157
3161
|
},
|
|
3158
3162
|
{
|
|
3159
3163
|
"path": "skills/lit-handoff/SKILL.md",
|
|
3160
|
-
"sha256": "
|
|
3164
|
+
"sha256": "d49141a84537cfa5dc36be0adb72fc57fb6ab4af97c1a75e82b84b4fa5983dd6"
|
|
3161
3165
|
},
|
|
3162
3166
|
{
|
|
3163
3167
|
"path": "skills/lit-humanizer/NOTICE",
|
|
@@ -4149,7 +4153,7 @@
|
|
|
4149
4153
|
},
|
|
4150
4154
|
{
|
|
4151
4155
|
"path": "skills/visual-qa/references/complete-contract.md",
|
|
4152
|
-
"sha256": "
|
|
4156
|
+
"sha256": "65b9754533bf3a07fc32d2e6a4b479d885e4bc7bcd25030c1dd36418db748eeb"
|
|
4153
4157
|
},
|
|
4154
4158
|
{
|
|
4155
4159
|
"path": "skills/visual-qa/schemas/evidence-manifest-v1alpha1.schema.json",
|
|
@@ -445,7 +445,7 @@ isolated Hermes home. Use `--hermes-home`; never `--home`.
|
|
|
445
445
|
|
|
446
446
|
```text
|
|
447
447
|
$ node packages/lithermes-installer/bin/lithermes.js install --yes --offline --no-hud --hermes-home "$ISOLATED"
|
|
448
|
-
Installed LitHermes 1.0.
|
|
448
|
+
Installed LitHermes 1.0.15
|
|
449
449
|
plugin: $ISOLATED/plugins/lithermes
|
|
450
450
|
model config: updated
|
|
451
451
|
|
|
@@ -462,7 +462,7 @@ installed payload: PASS
|
|
|
462
462
|
enabled config: PASS
|
|
463
463
|
|
|
464
464
|
$ hermes lithermes doctor
|
|
465
|
-
[OK] plugin.yaml readable (version 1.0.
|
|
465
|
+
[OK] plugin.yaml readable (version 1.0.15)
|
|
466
466
|
[OK] skills bundled: 36
|
|
467
467
|
[OK] litgoal durable runtime importable
|
|
468
468
|
```
|
|
@@ -62,6 +62,7 @@ on its own, before any other reply content:
|
|
|
62
62
|
| direct skill | explicit `lithermes:lit-handoff` | Read this adapter and then the original source in full. | Do not invent state that was not inspected. |
|
|
63
63
|
| slash command | `/lit-handoff [focus]` | Inject the exact source plus a redacted focus as inert data. | The command handler itself must not write workspace files. |
|
|
64
64
|
| exact natural route | the complete user message is `handoff` | Use the same source and named banner exactly once for that turn. | Near-misses, code spans, paths, and child-agent messages must not activate. |
|
|
65
|
+
| automatic handoff | the user switched it on with `/lit-handoff auto on <percent>` or `LITHERMES_AUTO_HANDOFF=1` plus `LITHERMES_AUTO_HANDOFF_PERCENT`, and a model call passed that percent | `auto_handoff.py` adds one `<lithermes-auto-handoff>` block to the next turn. Follow the original source, put the `auto-handoff-id:` line from the block near the top of the file, and tell the user the one plain line the block names. | Off by default, no built-in percent, one directive per crossing, delegate children never receive it. After a compaction the next turn gets a bounded digest, and only a file that carries this session's id and was written after the block is loaded. |
|
|
65
66
|
|
|
66
67
|
## #contract.procedure
|
|
67
68
|
|
|
@@ -138,6 +139,12 @@ limitations_channel: inline
|
|
|
138
139
|
|
|
139
140
|
# Hermes Adapter Notes
|
|
140
141
|
|
|
142
|
+
`auto_handoff.py` owns the switch and the automatic path: `/lit-handoff auto on
|
|
143
|
+
<percent>`, `auto off` and `auto status` answer in plain text without a model
|
|
144
|
+
call, and `status` and `doctor` print an `Automatic handoff` line. A plugin
|
|
145
|
+
cannot start compaction on Hermes, so the user runs the compact command, or
|
|
146
|
+
Hermes compacts on its own threshold.
|
|
147
|
+
|
|
141
148
|
`handoff.py` resolves the installed source root at runtime, reads the source
|
|
142
149
|
without rewriting it, and injects it as authoritative bundled guidance. The
|
|
143
150
|
slash command and exact natural route are side-effect-free until the model has
|
|
@@ -485,7 +485,7 @@ isolated Hermes home. Use `--hermes-home`; never `--home`.
|
|
|
485
485
|
|
|
486
486
|
```text
|
|
487
487
|
$ node packages/lithermes-installer/bin/lithermes.js install --yes --offline --no-hud --hermes-home "$ISOLATED"
|
|
488
|
-
Installed LitHermes 1.0.
|
|
488
|
+
Installed LitHermes 1.0.15
|
|
489
489
|
plugin: $ISOLATED/plugins/lithermes
|
|
490
490
|
model config: updated
|
|
491
491
|
|
|
@@ -502,7 +502,7 @@ installed payload: PASS
|
|
|
502
502
|
enabled config: PASS
|
|
503
503
|
|
|
504
504
|
$ hermes lithermes doctor
|
|
505
|
-
[OK] plugin.yaml readable (version 1.0.
|
|
505
|
+
[OK] plugin.yaml readable (version 1.0.15)
|
|
506
506
|
[OK] skills bundled: 36
|
|
507
507
|
[OK] litgoal durable runtime importable
|
|
508
508
|
```
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.15"><title>release: 1.0.15</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.15</text></g></svg>
|
|
@@ -3,7 +3,8 @@ const GPT6_LEAD_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max", "ult
|
|
|
3
3
|
const ASTRA_EFFORTS = GPT6_LEAD_EFFORTS;
|
|
4
4
|
const PARENT_MODEL = ASTRA_MODEL;
|
|
5
5
|
const PARENT_EFFORT = "xhigh";
|
|
6
|
-
const SOL_MODEL = "gpt-6-sol";
|
|
6
|
+
const SOL_MODEL = "gpt-6.1-sol";
|
|
7
|
+
const PREVIOUS_SOL_MODEL = "gpt-6-sol";
|
|
7
8
|
const CHILD_MODEL = "gpt-6-luna";
|
|
8
9
|
const LEGACY_SOL_MODEL = "gpt-5.6-sol";
|
|
9
10
|
const LEGACY_CHILD_MODEL = "gpt-5.6-luna";
|
|
@@ -18,6 +19,7 @@ const LEGACY_SOL_EFFORTS = new Set(["high", "xhigh"]);
|
|
|
18
19
|
const MODEL_EFFORTS = new Map([
|
|
19
20
|
[ASTRA_MODEL, GPT6_LEAD_EFFORTS],
|
|
20
21
|
[SOL_MODEL, GPT6_LEAD_EFFORTS],
|
|
22
|
+
[PREVIOUS_SOL_MODEL, GPT6_LEAD_EFFORTS],
|
|
21
23
|
[CHILD_MODEL, GPT6_LUNA_EFFORTS],
|
|
22
24
|
[LEGACY_SOL_MODEL, LEGACY_SOL_EFFORTS],
|
|
23
25
|
[LEGACY_CHILD_MODEL, LEGACY_LUNA_EFFORTS],
|
|
@@ -51,6 +53,11 @@ const ROUTE_CATALOG = {
|
|
|
51
53
|
model: SOL_MODEL,
|
|
52
54
|
note: effort === PARENT_EFFORT ? "coding lead alternative" : "Sol Responses effort",
|
|
53
55
|
})),
|
|
56
|
+
...["low", "medium", "high", "xhigh", "max", "ultra"].map((effort) => ({
|
|
57
|
+
effort,
|
|
58
|
+
model: PREVIOUS_SOL_MODEL,
|
|
59
|
+
note: "previous-generation Sol lead route",
|
|
60
|
+
})),
|
|
54
61
|
...["low", "medium", "high", "xhigh", "max"].map((effort) => ({
|
|
55
62
|
effort,
|
|
56
63
|
model: CHILD_MODEL,
|
|
@@ -403,6 +410,7 @@ module.exports = {
|
|
|
403
410
|
MANAGED_PROVIDER_MODELS,
|
|
404
411
|
MODEL_IDS,
|
|
405
412
|
OPENAI_PROVIDER_ID,
|
|
413
|
+
PREVIOUS_SOL_MODEL,
|
|
406
414
|
SOL_MODEL,
|
|
407
415
|
PROVIDER_IDS,
|
|
408
416
|
ROUTE_CATALOG,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|