honcho-cli 0.1.4__tar.gz → 0.2.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.
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/.gitignore +4 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/CHANGELOG.md +23 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/PKG-INFO +15 -3
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/README.md +13 -1
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/pyproject.toml +2 -2
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/__init__.py +1 -1
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/_help.py +9 -4
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/conclusion.py +133 -28
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/peer.py +52 -11
- honcho_cli-0.2.0/src/honcho_cli/commands/scope.py +262 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/setup.py +9 -2
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/workspace.py +83 -3
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/common.py +41 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/config.py +14 -1
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/main.py +2 -0
- honcho_cli-0.2.0/src/honcho_cli/recall.py +94 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_commands.py +214 -0
- honcho_cli-0.2.0/tests/test_conclusion_attribution.py +142 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_config.py +10 -1
- honcho_cli-0.2.0/tests/test_recall.py +62 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/scripts/generate_cli_docs.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/branding.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/__init__.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/config_cmd.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/message.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/session.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/commands/stack.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/__init__.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/docker.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/env.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/health.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/profile.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/setup.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/templates/__init__.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/templates/docker-compose.yml +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/local/templates/init.sql +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/oauth.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/output.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/update_check.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/src/honcho_cli/validation.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/__init__.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/conftest.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_common.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_local.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_oauth.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_output.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_setup.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_start.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/tests/test_validation.py +0 -0
- {honcho_cli-0.1.4 → honcho_cli-0.2.0}/uv.lock +0 -0
|
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-09-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- CLI requests carry `X-Honcho-Host: honcho-cli/<version> (<platform>)` so telemetry counts them as CLI traffic rather than direct SDK use (#1181)
|
|
15
|
+
- Conclusion attribution on `honcho conclusion list` and `honcho conclusion search` (Honcho v3.2.0+): every row now carries `level`, `source_ids` and `times_derived`, so an extracted fact is distinguishable from a dreamed one. Table output shows a premise count; `--json` keeps the full id list, ready to pipe into `honcho conclusion get` (#952)
|
|
16
|
+
- `honcho conclusion get <id>...` fetches conclusions by ID from anywhere in the workspace, with no observer/observed pair required. Feed it a conclusion's `source_ids` to walk a reasoning chain down to the explicit facts underneath it; IDs that no longer exist are reported rather than failing the command (#952)
|
|
17
|
+
- `honcho conclusion derived <id>` lists what was built on top of a conclusion — the same edge walked upward. Worth checking before deleting or correcting a fact (#952)
|
|
18
|
+
- `--level` on `honcho conclusion list` and `honcho conclusion search`, and `--derived-from` on `honcho conclusion list` (#952)
|
|
19
|
+
- `--evidence` on `honcho peer chat` and `honcho workspace chat` (Honcho v3.2.0+): reports the conclusions and messages the dialectic read and the tools it called. Evidence is collated from what the agent accessed rather than reported by the model, so it over-reports and costs no extra model tokens. Messages are summarised per session, since evidence carries their identity but not their text (#1129)
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Requires `honcho-ai` 2.5.0 (was 2.4.0), which carries the attribution fields and the evidence-bearing chat response
|
|
24
|
+
|
|
25
|
+
## [0.1.5] - 2026-09-09
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `honcho scope` command group: `list`, `create`, `inspect`, `sessions`, `add-sessions`, `remove-session`, `status` (Honcho v3.1.0+) (#1162)
|
|
30
|
+
- `honcho workspace chat` for reasoned questions across all peers (Honcho v3.1.0+) (#1150)
|
|
31
|
+
- `--scope` on `honcho workspace chat` and `honcho peer chat`, and `--sessions` on `honcho peer chat`, to confine recall (Honcho v3.1.0+) (#1151)
|
|
32
|
+
|
|
10
33
|
## [0.1.4] - 2026-08-26
|
|
11
34
|
|
|
12
35
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: honcho-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A terminal for Honcho — memory that reasons.
|
|
5
5
|
Project-URL: Homepage, https://github.com/plastic-labs/honcho
|
|
6
6
|
Project-URL: Repository, https://github.com/plastic-labs/honcho
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
14
14
|
Classifier: Topic :: Software Development :: Libraries
|
|
15
15
|
Requires-Python: >=3.11
|
|
16
16
|
Requires-Dist: click>=8.0.0
|
|
17
|
-
Requires-Dist: honcho-ai>=2.
|
|
17
|
+
Requires-Dist: honcho-ai>=2.5.0
|
|
18
18
|
Requires-Dist: httpx>=0.27.0
|
|
19
19
|
Requires-Dist: rich>=13.0.0
|
|
20
20
|
Requires-Dist: typer>=0.15.0
|
|
@@ -100,6 +100,7 @@ honcho stop --wipe # also delete volumes
|
|
|
100
100
|
| `honcho workspace list` | List accessible workspaces |
|
|
101
101
|
| `honcho workspace create <id>` | Create or get a workspace |
|
|
102
102
|
| `honcho workspace inspect` | Peers, sessions, config for a workspace |
|
|
103
|
+
| `honcho workspace chat <query>` | Query the dialectic across all peers (optional `-s` / `--scope` / `--reasoning`) |
|
|
103
104
|
| `honcho workspace search <query>` | Search messages across workspace |
|
|
104
105
|
| `honcho workspace queue-status` | Deriver queue status (filter with `--observer` / `--sender`) |
|
|
105
106
|
| `honcho workspace delete <id>` | Delete a workspace. Use `--dry-run` to preview, `--cascade` to also delete sessions, `--yes` to skip the confirm prompt |
|
|
@@ -112,7 +113,7 @@ honcho stop --wipe # also delete volumes
|
|
|
112
113
|
| `honcho peer create <id>` | Create or get a peer |
|
|
113
114
|
| `honcho peer inspect <id>` | Card, session count, recent conclusions |
|
|
114
115
|
| `honcho peer card <id>` | Raw peer card content |
|
|
115
|
-
| `honcho peer chat <query>` | Query the dialectic about a peer (peer via `-p` / `HONCHO_PEER_ID`) |
|
|
116
|
+
| `honcho peer chat <query>` | Query the dialectic about a peer (peer via `-p` / `HONCHO_PEER_ID`; optional `--scope` / `--sessions`) |
|
|
116
117
|
| `honcho peer representation <id>` | Formatted representation |
|
|
117
118
|
| `honcho peer search <query>` | Search a peer's messages (peer via `-p` / `HONCHO_PEER_ID`) |
|
|
118
119
|
| `honcho peer get-metadata <id>` / `set-metadata` | Metadata operations |
|
|
@@ -133,6 +134,17 @@ honcho stop --wipe # also delete volumes
|
|
|
133
134
|
| `honcho session get-metadata <id>` / `set-metadata` | Metadata operations |
|
|
134
135
|
| `honcho session delete <id>` | Destructive; requires `--yes` |
|
|
135
136
|
|
|
137
|
+
### Scopes
|
|
138
|
+
|
|
139
|
+
| Command | Description |
|
|
140
|
+
|---------|-------------|
|
|
141
|
+
| `honcho scope list` | List scopes in the workspace |
|
|
142
|
+
| `honcho scope create <name>` | Create or get a scope (`--sessions id,...` to add members, `--metadata`) |
|
|
143
|
+
| `honcho scope inspect <name>` | Metadata, member sessions, backfill summary. Never creates a missing scope |
|
|
144
|
+
| `honcho scope sessions <name>` | Member sessions, longest-standing first |
|
|
145
|
+
| `honcho scope add-sessions <name> <ids...>` / `remove-session <name> <id>` | Membership; history backfills asynchronously |
|
|
146
|
+
| `honcho scope status <name>` | Per-session backfill state — recall is complete once nothing is `pending` |
|
|
147
|
+
|
|
136
148
|
### Messages
|
|
137
149
|
|
|
138
150
|
| Command | Description |
|
|
@@ -75,6 +75,7 @@ honcho stop --wipe # also delete volumes
|
|
|
75
75
|
| `honcho workspace list` | List accessible workspaces |
|
|
76
76
|
| `honcho workspace create <id>` | Create or get a workspace |
|
|
77
77
|
| `honcho workspace inspect` | Peers, sessions, config for a workspace |
|
|
78
|
+
| `honcho workspace chat <query>` | Query the dialectic across all peers (optional `-s` / `--scope` / `--reasoning`) |
|
|
78
79
|
| `honcho workspace search <query>` | Search messages across workspace |
|
|
79
80
|
| `honcho workspace queue-status` | Deriver queue status (filter with `--observer` / `--sender`) |
|
|
80
81
|
| `honcho workspace delete <id>` | Delete a workspace. Use `--dry-run` to preview, `--cascade` to also delete sessions, `--yes` to skip the confirm prompt |
|
|
@@ -87,7 +88,7 @@ honcho stop --wipe # also delete volumes
|
|
|
87
88
|
| `honcho peer create <id>` | Create or get a peer |
|
|
88
89
|
| `honcho peer inspect <id>` | Card, session count, recent conclusions |
|
|
89
90
|
| `honcho peer card <id>` | Raw peer card content |
|
|
90
|
-
| `honcho peer chat <query>` | Query the dialectic about a peer (peer via `-p` / `HONCHO_PEER_ID`) |
|
|
91
|
+
| `honcho peer chat <query>` | Query the dialectic about a peer (peer via `-p` / `HONCHO_PEER_ID`; optional `--scope` / `--sessions`) |
|
|
91
92
|
| `honcho peer representation <id>` | Formatted representation |
|
|
92
93
|
| `honcho peer search <query>` | Search a peer's messages (peer via `-p` / `HONCHO_PEER_ID`) |
|
|
93
94
|
| `honcho peer get-metadata <id>` / `set-metadata` | Metadata operations |
|
|
@@ -108,6 +109,17 @@ honcho stop --wipe # also delete volumes
|
|
|
108
109
|
| `honcho session get-metadata <id>` / `set-metadata` | Metadata operations |
|
|
109
110
|
| `honcho session delete <id>` | Destructive; requires `--yes` |
|
|
110
111
|
|
|
112
|
+
### Scopes
|
|
113
|
+
|
|
114
|
+
| Command | Description |
|
|
115
|
+
|---------|-------------|
|
|
116
|
+
| `honcho scope list` | List scopes in the workspace |
|
|
117
|
+
| `honcho scope create <name>` | Create or get a scope (`--sessions id,...` to add members, `--metadata`) |
|
|
118
|
+
| `honcho scope inspect <name>` | Metadata, member sessions, backfill summary. Never creates a missing scope |
|
|
119
|
+
| `honcho scope sessions <name>` | Member sessions, longest-standing first |
|
|
120
|
+
| `honcho scope add-sessions <name> <ids...>` / `remove-session <name> <id>` | Membership; history backfills asynchronously |
|
|
121
|
+
| `honcho scope status <name>` | Per-session backfill state — recall is complete once nothing is `pending` |
|
|
122
|
+
|
|
111
123
|
### Messages
|
|
112
124
|
|
|
113
125
|
| Command | Description |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "honcho-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A terminal for Honcho — memory that reasons."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -19,7 +19,7 @@ classifiers = [
|
|
|
19
19
|
dependencies = [
|
|
20
20
|
"click>=8.0.0",
|
|
21
21
|
"typer>=0.15.0",
|
|
22
|
-
"honcho-ai>=2.
|
|
22
|
+
"honcho-ai>=2.5.0",
|
|
23
23
|
"rich>=13.0.0",
|
|
24
24
|
"httpx>=0.27.0",
|
|
25
25
|
]
|
|
@@ -80,30 +80,35 @@ def print_welcome(console: Console) -> None:
|
|
|
80
80
|
("[dim]pattern[/dim]", r"[dim]honcho <command> \[args] \[-w workspace] \[-p peer] \[-s session][/dim]"),
|
|
81
81
|
("[dim]example[/dim]", "[dim]honcho peer chat \"what does alice prefer?\" -p alice -w agents[/dim]"),
|
|
82
82
|
("", ""),
|
|
83
|
-
("workspace", "list · create · search · delete · inspect · queue-status"),
|
|
83
|
+
("workspace", "list · create · search · chat · delete · inspect · queue-status"),
|
|
84
84
|
("peer", "list · create · search · inspect · card · chat"),
|
|
85
85
|
("", "get-metadata · set-metadata · representation"),
|
|
86
86
|
("session", "list · create · search · delete · inspect · view · add-peers"),
|
|
87
87
|
("", "context · get-metadata · set-metadata · peers"),
|
|
88
88
|
("", "remove-peers · representation · summaries"),
|
|
89
|
+
("scope", "list · create · inspect · sessions · add-sessions"),
|
|
90
|
+
("", "remove-session · status"),
|
|
89
91
|
("message", "list · create · get"),
|
|
90
92
|
("conclusion", "list · create · search · delete"),
|
|
91
93
|
("config", "inspect current configuration"),
|
|
92
94
|
]
|
|
93
95
|
memory_rows = [
|
|
96
|
+
("honcho workspace chat \"...\" -w <workspace>", "query the Dialectic across all peers"),
|
|
94
97
|
("honcho peer chat \"...\" -p <peer> -w <workspace>", "query the Dialectic about a peer"),
|
|
95
98
|
("honcho peer inspect -p <peer> -w <workspace>", "dashboard: peer card + recent conclusions + configuration"),
|
|
96
99
|
("honcho peer representation -p <peer> -w <workspace>", "global peer representation"),
|
|
97
100
|
("honcho peer representation -p <peer> -w <workspace> -s <session>", "session-scoped peer representation"),
|
|
101
|
+
("honcho scope list -w <workspace>", "which session sets exist; inspect one for members + backfill"),
|
|
102
|
+
("honcho peer chat \"...\" -p <peer> --scope <scope>", "answer from one scope's sessions only"),
|
|
98
103
|
("honcho peer card -p <peer> -w <workspace>", "synthesized identity: traits, preferences, instructions"),
|
|
99
104
|
("honcho conclusion list -p <peer> -w <workspace>", "browse peer conclusions"),
|
|
100
105
|
("honcho session view / context -s <session>", "transcript, or what an agent would see"),
|
|
101
106
|
("honcho workspace queue-status", "is the deriver processing?"),
|
|
102
107
|
]
|
|
103
108
|
option_rows = [
|
|
104
|
-
("-w / --workspace", "
|
|
105
|
-
("-p / --peer", "
|
|
106
|
-
("-s / --session", "
|
|
109
|
+
("-w / --workspace", "target a workspace"),
|
|
110
|
+
("-p / --peer", "target a peer"),
|
|
111
|
+
("-s / --session", "target a session"),
|
|
107
112
|
("--json", "force JSON output for scripts and agents"),
|
|
108
113
|
("--help", "show help for any command (e.g. honcho peer --help)"),
|
|
109
114
|
]
|
|
@@ -18,6 +18,43 @@ app = typer.Typer(cls=HonchoTyperGroup, help="List, search, create, and delete p
|
|
|
18
18
|
add_common_options(app)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
_LEVELS = ("explicit", "deductive", "inductive", "contradiction")
|
|
22
|
+
|
|
23
|
+
#: Columns carrying a conclusion's attribution, shown alongside the content.
|
|
24
|
+
_ATTRIBUTION_COLUMNS = ["id", "level", "source_ids", "times_derived", "content"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _format_conclusion(c, workspace_id: str | None) -> dict:
|
|
28
|
+
"""Shape a Conclusion for output.
|
|
29
|
+
|
|
30
|
+
``level``, ``source_ids`` and ``times_derived`` are the attribution the
|
|
31
|
+
server started returning in Honcho v3.2.0. In table mode ``source_ids``
|
|
32
|
+
collapses to a count, since the ids are nanoids and a list of them makes
|
|
33
|
+
the row unreadable; JSON mode keeps the full list so it can be piped back
|
|
34
|
+
into ``honcho conclusion get``.
|
|
35
|
+
"""
|
|
36
|
+
source_ids = c.source_ids or []
|
|
37
|
+
return {
|
|
38
|
+
"id": c.id,
|
|
39
|
+
"level": c.level,
|
|
40
|
+
"source_ids": source_ids if use_json() else len(source_ids),
|
|
41
|
+
"times_derived": c.times_derived,
|
|
42
|
+
"content": c.content if use_json() else c.content[:160],
|
|
43
|
+
"workspace_id": workspace_id,
|
|
44
|
+
"observer_id": c.observer_id,
|
|
45
|
+
"observed_id": c.observed_id,
|
|
46
|
+
"session_id": c.session_id,
|
|
47
|
+
"created_at": str(c.created_at),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _validate_level(level: str | None) -> str | None:
|
|
52
|
+
if level and level not in _LEVELS:
|
|
53
|
+
print_error("INVALID_LEVEL", f"--level must be one of: {', '.join(_LEVELS)}")
|
|
54
|
+
raise typer.Exit(1)
|
|
55
|
+
return level
|
|
56
|
+
|
|
57
|
+
|
|
21
58
|
def _require_observer(observer: str | None) -> str:
|
|
22
59
|
"""Resolve observer peer ID; emit combined error if peer+workspace both missing."""
|
|
23
60
|
config = get_resolved_config()
|
|
@@ -39,6 +76,16 @@ def list_conclusions(
|
|
|
39
76
|
observer: Optional[str] = typer.Option(None, "--observer", help="Observer peer ID"),
|
|
40
77
|
observed: Optional[str] = typer.Option(None, "--observed", help="Observed peer ID"),
|
|
41
78
|
limit: int = typer.Option(10, "--limit", help="Max results"),
|
|
79
|
+
level: Optional[str] = typer.Option(
|
|
80
|
+
None,
|
|
81
|
+
"--level",
|
|
82
|
+
help="Only this reasoning level: explicit, deductive, inductive, contradiction",
|
|
83
|
+
),
|
|
84
|
+
derived_from: Optional[str] = typer.Option(
|
|
85
|
+
None,
|
|
86
|
+
"--derived-from",
|
|
87
|
+
help="Only conclusions derived from this conclusion ID",
|
|
88
|
+
),
|
|
42
89
|
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
|
43
90
|
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
|
|
44
91
|
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
|
@@ -46,31 +93,27 @@ def list_conclusions(
|
|
|
46
93
|
"""List conclusions."""
|
|
47
94
|
|
|
48
95
|
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
|
|
96
|
+
_validate_level(level)
|
|
49
97
|
observer = _require_observer(observer)
|
|
50
98
|
client, config = get_client()
|
|
51
99
|
|
|
52
100
|
p = client.peer(observer)
|
|
53
101
|
|
|
102
|
+
filters: dict = {}
|
|
103
|
+
if level:
|
|
104
|
+
filters["level"] = level
|
|
105
|
+
if derived_from:
|
|
106
|
+
filters["source_ids"] = {"contains": derived_from}
|
|
107
|
+
|
|
54
108
|
try:
|
|
55
109
|
if observed:
|
|
56
110
|
scope = p.conclusions_of(observed)
|
|
57
111
|
else:
|
|
58
112
|
scope = p.conclusions
|
|
59
113
|
|
|
60
|
-
conclusions = scope.list(size=limit).items
|
|
61
|
-
items = [
|
|
62
|
-
|
|
63
|
-
"id": c.id,
|
|
64
|
-
"content": c.content if use_json() else c.content[:200],
|
|
65
|
-
"workspace_id": config.workspace_id,
|
|
66
|
-
"observer_id": c.observer_id,
|
|
67
|
-
"observed_id": c.observed_id,
|
|
68
|
-
"session_id": c.session_id,
|
|
69
|
-
"created_at": str(c.created_at),
|
|
70
|
-
}
|
|
71
|
-
for c in conclusions
|
|
72
|
-
]
|
|
73
|
-
print_result(items, columns=["id", "content", "workspace_id", "observer_id", "observed_id", "session_id", "created_at"], title="Conclusions")
|
|
114
|
+
conclusions = scope.list(size=limit, filters=filters or None).items
|
|
115
|
+
items = [_format_conclusion(c, config.workspace_id) for c in conclusions]
|
|
116
|
+
print_result(items, columns=_ATTRIBUTION_COLUMNS + ["observed_id", "created_at"], title="Conclusions")
|
|
74
117
|
except Exception as e:
|
|
75
118
|
_handle_error(e, "conclusion", "list")
|
|
76
119
|
|
|
@@ -81,6 +124,11 @@ def search(
|
|
|
81
124
|
observer: Optional[str] = typer.Option(None, "--observer", help="Observer peer ID"),
|
|
82
125
|
observed: Optional[str] = typer.Option(None, "--observed", help="Observed peer ID"),
|
|
83
126
|
top_k: int = typer.Option(10, help="Max results"),
|
|
127
|
+
level: Optional[str] = typer.Option(
|
|
128
|
+
None,
|
|
129
|
+
"--level",
|
|
130
|
+
help="Only this reasoning level: explicit, deductive, inductive, contradiction",
|
|
131
|
+
),
|
|
84
132
|
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
|
85
133
|
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
|
|
86
134
|
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
|
@@ -88,6 +136,7 @@ def search(
|
|
|
88
136
|
"""Semantic search over conclusions."""
|
|
89
137
|
|
|
90
138
|
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
|
|
139
|
+
_validate_level(level)
|
|
91
140
|
observer = _require_observer(observer)
|
|
92
141
|
client, config = get_client()
|
|
93
142
|
|
|
@@ -99,24 +148,80 @@ def search(
|
|
|
99
148
|
else:
|
|
100
149
|
scope = p.conclusions
|
|
101
150
|
|
|
102
|
-
results = scope.query(query, top_k=top_k)
|
|
103
|
-
items = [
|
|
104
|
-
|
|
105
|
-
"id": c.id,
|
|
106
|
-
"content": c.content if use_json() else c.content[:200],
|
|
107
|
-
"workspace_id": config.workspace_id,
|
|
108
|
-
"observer_id": c.observer_id,
|
|
109
|
-
"observed_id": c.observed_id,
|
|
110
|
-
"session_id": c.session_id,
|
|
111
|
-
"created_at": str(c.created_at),
|
|
112
|
-
}
|
|
113
|
-
for c in results
|
|
114
|
-
]
|
|
115
|
-
print_result(items, columns=["id", "content", "workspace_id", "session_id", "created_at"], title=f"Conclusion search: {query}")
|
|
151
|
+
results = scope.query(query, top_k=top_k, filters={"level": level} if level else None)
|
|
152
|
+
items = [_format_conclusion(c, config.workspace_id) for c in results]
|
|
153
|
+
print_result(items, columns=_ATTRIBUTION_COLUMNS + ["created_at"], title=f"Conclusion search: {query}")
|
|
116
154
|
except Exception as e:
|
|
117
155
|
_handle_error(e, "conclusion", "search")
|
|
118
156
|
|
|
119
157
|
|
|
158
|
+
@app.command()
|
|
159
|
+
def get(
|
|
160
|
+
conclusion_ids: list[str] = typer.Argument(help="Conclusion IDs to fetch"),
|
|
161
|
+
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
|
162
|
+
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Fetch conclusions by ID, from anywhere in the workspace.
|
|
165
|
+
|
|
166
|
+
Pass a conclusion's source_ids to see the premises it was derived from,
|
|
167
|
+
and repeat to walk a reasoning chain down to the explicit facts it rests
|
|
168
|
+
on. IDs that no longer exist are reported rather than failing the command.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
handle_cmd_flags(json_output=json_output, workspace=workspace)
|
|
172
|
+
client, config = get_client()
|
|
173
|
+
|
|
174
|
+
for cid in conclusion_ids:
|
|
175
|
+
validate_resource_id(cid, "conclusion")
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
conclusions = client.conclusions.get_many(list(conclusion_ids))
|
|
179
|
+
found = {c.id for c in conclusions}
|
|
180
|
+
missing = [cid for cid in conclusion_ids if cid not in found]
|
|
181
|
+
|
|
182
|
+
items = [_format_conclusion(c, config.workspace_id) for c in conclusions]
|
|
183
|
+
print_result(items, columns=_ATTRIBUTION_COLUMNS + ["observed_id", "created_at"], title="Conclusions")
|
|
184
|
+
if missing:
|
|
185
|
+
print_error(
|
|
186
|
+
"MISSING_CONCLUSIONS",
|
|
187
|
+
f"Not in this workspace (consolidated or deleted): {', '.join(missing)}",
|
|
188
|
+
)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
_handle_error(e, "conclusion", "get")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@app.command()
|
|
194
|
+
def derived(
|
|
195
|
+
conclusion_id: str = typer.Argument(help="The premise conclusion ID"),
|
|
196
|
+
limit: int = typer.Option(10, "--limit", help="Max results"),
|
|
197
|
+
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
|
198
|
+
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
|
199
|
+
) -> None:
|
|
200
|
+
"""List the conclusions derived FROM a conclusion.
|
|
201
|
+
|
|
202
|
+
Walks the reasoning tree upward (premise -> conclusion); `conclusion get`
|
|
203
|
+
on a conclusion's source_ids walks it downward. Worth checking before
|
|
204
|
+
deleting or correcting a fact.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
handle_cmd_flags(json_output=json_output, workspace=workspace)
|
|
208
|
+
validate_resource_id(conclusion_id, "conclusion")
|
|
209
|
+
client, config = get_client()
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
page = client.conclusions.list(
|
|
213
|
+
size=limit, filters={"source_ids": {"contains": conclusion_id}}
|
|
214
|
+
)
|
|
215
|
+
items = [_format_conclusion(c, config.workspace_id) for c in page.items]
|
|
216
|
+
print_result(
|
|
217
|
+
items,
|
|
218
|
+
columns=_ATTRIBUTION_COLUMNS + ["observed_id", "created_at"],
|
|
219
|
+
title=f"Derived from {conclusion_id}",
|
|
220
|
+
)
|
|
221
|
+
except Exception as e:
|
|
222
|
+
_handle_error(e, "conclusion", "derived")
|
|
223
|
+
|
|
224
|
+
|
|
120
225
|
@app.command()
|
|
121
226
|
def create(
|
|
122
227
|
content: str = typer.Argument(help="Conclusion content or JSON payload"),
|
|
@@ -9,12 +9,13 @@ import typer
|
|
|
9
9
|
|
|
10
10
|
from honcho.api_types import PeerConfig
|
|
11
11
|
|
|
12
|
-
from honcho_cli.commands.workspace import _config_to_dict, _handle_error, _raw_list
|
|
12
|
+
from honcho_cli.commands.workspace import _config_to_dict, _handle_chat_error, _handle_error, _raw_list
|
|
13
13
|
from honcho_cli.output import print_error, print_result, use_json
|
|
14
|
+
from honcho_cli.recall import parse_csv_repeatable, reject_incompatible_recall, scope_for_sdk
|
|
14
15
|
from honcho_cli.validation import validate_resource_id
|
|
15
16
|
|
|
16
17
|
from honcho_cli._help import HonchoTyperGroup
|
|
17
|
-
from honcho_cli.common import add_common_options, get_client, get_resolved_config, handle_cmd_flags
|
|
18
|
+
from honcho_cli.common import add_common_options, format_evidence, get_client, get_flag_overrides, get_resolved_config, handle_cmd_flags
|
|
18
19
|
|
|
19
20
|
app = typer.Typer(cls=HonchoTyperGroup, help="List, create, chat with, search, and manage peers and their representations.")
|
|
20
21
|
add_common_options(app)
|
|
@@ -130,6 +131,21 @@ def chat(
|
|
|
130
131
|
query: str = typer.Argument(help="Question to ask about the peer"),
|
|
131
132
|
target: Optional[str] = typer.Option(None, help="Target peer for perspective"),
|
|
132
133
|
reasoning: Optional[str] = typer.Option(None, "--reasoning", "-r", help="Reasoning level: minimal, low, medium, high, max"),
|
|
134
|
+
scope: Optional[list[str]] = typer.Option(
|
|
135
|
+
None,
|
|
136
|
+
"--scope",
|
|
137
|
+
help="Recall only from this scope. Repeat or comma-separate for several (explicit conclusions only). Excludes -s and --sessions.",
|
|
138
|
+
),
|
|
139
|
+
sessions: Optional[list[str]] = typer.Option(
|
|
140
|
+
None,
|
|
141
|
+
"--sessions",
|
|
142
|
+
help="Recall only from these session IDs (repeat or comma-separate); explicit conclusions only. Excludes -s and --scope.",
|
|
143
|
+
),
|
|
144
|
+
evidence: bool = typer.Option(
|
|
145
|
+
False,
|
|
146
|
+
"--evidence",
|
|
147
|
+
help="Also report what the answer was built from: the conclusions and messages read and the tools called.",
|
|
148
|
+
),
|
|
133
149
|
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
|
134
150
|
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
|
|
135
151
|
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
|
|
@@ -139,25 +155,50 @@ def chat(
|
|
|
139
155
|
|
|
140
156
|
_REASONING_LEVELS = ("minimal", "low", "medium", "high", "max")
|
|
141
157
|
if reasoning and reasoning not in _REASONING_LEVELS:
|
|
142
|
-
from honcho_cli.output import print_error
|
|
143
158
|
print_error("INVALID_REASONING", f"--reasoning must be one of: {', '.join(_REASONING_LEVELS)}")
|
|
144
159
|
raise typer.Exit(1)
|
|
145
160
|
|
|
146
161
|
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer, session=session)
|
|
147
162
|
pid = _get_peer_id(None)
|
|
148
163
|
client, config = get_client()
|
|
149
|
-
|
|
164
|
+
scope_names = parse_csv_repeatable(scope, kind="scope", flag="--scope")
|
|
165
|
+
session_allowlist = parse_csv_repeatable(sessions, kind="session", flag="--sessions")
|
|
166
|
+
session_id = config.session_id or None
|
|
167
|
+
reject_incompatible_recall(
|
|
168
|
+
session_id=session_id,
|
|
169
|
+
scope=scope_names,
|
|
170
|
+
sessions=session_allowlist,
|
|
171
|
+
session_from_env=not get_flag_overrides()["session"],
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
chat_kwargs: dict[str, object] = {
|
|
175
|
+
"target": target,
|
|
176
|
+
"session": session_id,
|
|
177
|
+
"reasoning_level": reasoning or None,
|
|
178
|
+
}
|
|
179
|
+
scope_arg = scope_for_sdk(scope_names)
|
|
180
|
+
if scope_arg is not None:
|
|
181
|
+
chat_kwargs["scope"] = scope_arg
|
|
182
|
+
if session_allowlist is not None:
|
|
183
|
+
chat_kwargs["sessions"] = session_allowlist
|
|
150
184
|
|
|
151
185
|
try:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
186
|
+
p = client.peer(pid)
|
|
187
|
+
if not evidence:
|
|
188
|
+
response = p.chat(query, **chat_kwargs)
|
|
189
|
+
print_result({"peer_id": pid, "query": query, "response": response})
|
|
190
|
+
return
|
|
191
|
+
result = p.chat(query, include_evidence=True, **chat_kwargs)
|
|
192
|
+
print_result(
|
|
193
|
+
{
|
|
194
|
+
"peer_id": pid,
|
|
195
|
+
"query": query,
|
|
196
|
+
"response": result.content,
|
|
197
|
+
"evidence": format_evidence(result.evidence),
|
|
198
|
+
}
|
|
157
199
|
)
|
|
158
|
-
print_result({"peer_id": pid, "query": query, "response": response})
|
|
159
200
|
except Exception as e:
|
|
160
|
-
|
|
201
|
+
_handle_chat_error(e, "peer", pid)
|
|
161
202
|
|
|
162
203
|
|
|
163
204
|
@app.command()
|