@turmind/halo 1.1.3 → 1.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turmind/halo",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Halo — multi-agent collaborative workspace. CLI + TUI + HTTP/WS server in one package.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@ skills:
45
45
  - send-file
46
46
  - self
47
47
  - aws-knowledge
48
- - nova-web-search
48
+ - web-search
49
49
  - cron
50
50
  - acp
51
51
  - halo
@@ -35,6 +35,34 @@ secrets:
35
35
  # thinking.default → effort selected when Thinking is on (adaptive)
36
36
  # thinking.defaultBudgetTokens → budget selected when Thinking is on (manual)
37
37
  models:
38
+ - id: global.anthropic.claude-fable-5-1
39
+ displayName: Claude Fable 5.1
40
+ maxOutputTokens: 128000
41
+ contextWindow: 1000000
42
+ capabilities:
43
+ image: true
44
+ video: false
45
+ audio: false
46
+ promptCaching:
47
+ default: 1h
48
+ ttlPresets:
49
+ - { value: 5m, label: 5min }
50
+ - { value: 1h, label: 1hour }
51
+ thinking:
52
+ # Fable 5.1 — Mythos-class flagship refresh. Adaptive effort set
53
+ # confirmed by an invalid-value probe against Bedrock (2026-09-02):
54
+ # low / medium / high / xhigh / max, same as Fable 5. 1M context,
55
+ # 128K output. Smoke-tested on invoke (xhigh).
56
+ mode: adaptive
57
+ defaultEnabled: true
58
+ default: high # flagship-of-flagships — high by default; xhigh/max on demand
59
+ effortPresets:
60
+ - { value: low, label: Low }
61
+ - { value: medium, label: Medium }
62
+ - { value: high, label: High }
63
+ - { value: xhigh, label: XHigh }
64
+ - { value: max, label: Max }
65
+
38
66
  - id: global.anthropic.claude-fable-5
39
67
  displayName: Claude Fable 5
40
68
  maxOutputTokens: 128000
@@ -46,7 +46,7 @@ simply absent. Consequences you must apply:
46
46
  global skill for one workspace, copy the WHOLE folder to workspace scope
47
47
  (including resource files — remember: no per-file fallback), then edit.
48
48
  - **Built-in skills** (`agent`, `skill`, `workspace`, `cron`, `acp`, `send-file`,
49
- `self`, `aws-knowledge`, `nova-web-search`): their global copies are force re-seeded on every server
49
+ `self`, `aws-knowledge`, `web-search`): their global copies are force re-seeded on every server
50
50
  start — direct edits to those global files are lost. Customize at workspace
51
51
  scope instead.
52
52
  - **Disabled state** lives per-workspace in the `disabled_items` DB table, not
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: web-search
3
+ description: Real-time web search with two gears. Fast (default, Amazon Nova grounding, ~3s, cheap) — current events, news, prices, any routine lookup. Deep (--deep, OpenAI GPT-5.6 web_search via Bedrock Mantle, ~20-40s, token-expensive) — multi-round retrieval with first-hand sources and per-claim citations, for verifying facts behind important decisions or double-checking weak/contradictory fast results. Default to fast; escalate to deep only when the question deserves depth.
4
+ user-invocable: false
5
+ ---
6
+ # Web Search
7
+
8
+ One skill, two gears:
9
+
10
+ | Gear | Engine | Speed / cost | Use for |
11
+ |---|---|---|---|
12
+ | **fast** (default) | Amazon Nova 2 Lite `nova_grounding` | ~3 s, ~500 tokens | News, quotes, prices, routine lookups — the everyday default |
13
+ | **deep** (`--deep`) | OpenAI GPT-5.6 `web_search` (Bedrock Mantle) | ~20-40 s, 10-40k tokens | Fact verification before important conclusions; second opinion when fast results look weak, self-contradictory, or conflict with data you hold |
14
+
15
+ Deep gear does multi-round real retrieval (bilingual query rewriting, `site:`-scoped
16
+ searches against official sites), leans toward authoritative first-hand sources
17
+ (official announcements, news agencies), and attaches a citation to every claim —
18
+ **expensive and slow, but thorough and it doesn't make things up**.
19
+
20
+ ## Usage
21
+
22
+ ```bash
23
+ # fast (default) — everyday lookups
24
+ python3 ~/.halo/global/skills/web-search/search.py "What are the latest news about the China A-share market today?"
25
+
26
+ # deep — verify facts that must be right
27
+ python3 ~/.halo/global/skills/web-search/search.py --deep "What did the US Federal Reserve announce most recently about interest rates?"
28
+ ```
29
+
30
+ - Output: answer text + a `Sources:` URL list (gear/model/token usage on stderr).
31
+ - Deep gear default model is `luna` (reasoning effort=max — retrieval planning
32
+ maxed out). `--deep --model sol` is carpet-bombing research (auto-paired with
33
+ effort=xhigh; ~3x more search queries, roughly double the tokens) — reserve it
34
+ for "profile one stock / one topic exhaustively" jobs.
35
+ - Full natural-language questions beat keyword stubs in both gears
36
+ (`"RTX 5080"` ✗ → `"Tell me about the NVIDIA RTX 5080, including specs and price."` ✓).
37
+ English works best; Chinese works too — deep gear rewrites queries bilingually.
38
+ - Search output is still a lead, not gospel: for critical figures, `web_fetch`
39
+ the cited source URL and confirm before acting on it.
40
+ - Auth is automatic in both gears (machine AWS credentials — no API key to
41
+ configure or rotate). Deep gear needs the `aws-bedrock-token-generator` pip
42
+ package (one-time: `pip3 install --user aws-bedrock-token-generator`).
43
+ - A deep call can take 40 s — be patient in shell_exec. One clear question per
44
+ call; don't fan out parallel calls.
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """Web search with two gears.
3
+
4
+ fast (default): Amazon Nova 2 Lite + nova_grounding — ~3 s, ~500 tokens.
5
+ deep (--deep): OpenAI GPT-5.6 on Bedrock Mantle + server-side web_search —
6
+ multi-round retrieval, first-hand sources, ~20-40 s, 10-40k tokens.
7
+
8
+ Usage:
9
+ python3 search.py "your question"
10
+ python3 search.py --deep "your question" [--model sol|luna]
11
+
12
+ Auth is automatic in both gears (machine AWS credentials): fast uses boto3's
13
+ default credential chain; deep mints a 12-hour bearer token on every call via
14
+ aws-bedrock-token-generator — no API key to configure or rotate.
15
+ """
16
+ import json
17
+ import sys
18
+ import urllib.error
19
+ import urllib.request
20
+
21
+ MANTLE_URL = "https://bedrock-mantle.us-east-2.api.aws/openai/v1/responses"
22
+ MODELS = {"luna": "openai.gpt-5.6-luna", "sol": "openai.gpt-5.6-sol"}
23
+ # luna (default) runs effort=max to squeeze out its retrieval planning; sol's
24
+ # retrieval is already exhaustive at medium (measured 48 search queries on one
25
+ # question), so it gets xhigh to avoid pointless token doubling — max over
26
+ # xhigh measured only ~6% more reasoning tokens.
27
+ EFFORTS = {"openai.gpt-5.6-luna": "max", "openai.gpt-5.6-sol": "xhigh"}
28
+
29
+
30
+ def fast_search(question: str) -> None:
31
+ import boto3
32
+ from botocore.config import Config
33
+
34
+ bedrock = boto3.client(
35
+ "bedrock-runtime", region_name="us-east-1", config=Config(read_timeout=60)
36
+ )
37
+ response = bedrock.converse(
38
+ modelId="us.amazon.nova-2-lite-v1:0",
39
+ messages=[{"role": "user", "content": [{"text": question}]}],
40
+ toolConfig={"tools": [{"systemTool": {"name": "nova_grounding"}}]},
41
+ )
42
+ answer = ""
43
+ sources: list[str] = []
44
+ for content in response["output"]["message"]["content"]:
45
+ if "text" in content:
46
+ answer += content["text"]
47
+ elif "citationsContent" in content:
48
+ for c in content["citationsContent"].get("citations", []):
49
+ url = c.get("location", {}).get("web", {}).get("url", "")
50
+ if url and url not in sources:
51
+ sources.append(url)
52
+ print(answer)
53
+ if sources:
54
+ print("\nSources:")
55
+ for u in sources:
56
+ print(" -", u)
57
+ usage = response.get("usage", {})
58
+ print(f"\n[gear=fast model=nova-2-lite total_tokens={usage.get('totalTokens')}]", file=sys.stderr)
59
+
60
+
61
+ def deep_search(question: str, model: str) -> None:
62
+ from aws_bedrock_token_generator import provide_token
63
+
64
+ token = provide_token(region="us-east-2")
65
+ body = json.dumps(
66
+ {
67
+ "model": model,
68
+ "input": question,
69
+ "tools": [{"type": "web_search"}],
70
+ "reasoning": {"effort": EFFORTS[model]},
71
+ }
72
+ ).encode()
73
+ req = urllib.request.Request(
74
+ MANTLE_URL,
75
+ data=body,
76
+ headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
77
+ method="POST",
78
+ )
79
+ try:
80
+ with urllib.request.urlopen(req, timeout=180) as resp:
81
+ data = json.loads(resp.read())
82
+ except urllib.error.HTTPError as e:
83
+ print(f"HTTP {e.code}: {e.read().decode(errors='replace')[:2000]}", file=sys.stderr)
84
+ sys.exit(1)
85
+
86
+ texts: list[str] = []
87
+ sources: list[str] = []
88
+ for item in data.get("output", []):
89
+ if item.get("type") == "message":
90
+ for c in item.get("content", []):
91
+ if c.get("type") == "output_text":
92
+ texts.append(c.get("text", ""))
93
+ for a in c.get("annotations", []):
94
+ if a.get("type") == "url_citation":
95
+ u = a.get("url", "")
96
+ if u and u not in sources:
97
+ sources.append(u)
98
+ print("\n".join(texts))
99
+ if sources:
100
+ print("\nSources:")
101
+ for u in sources:
102
+ print(" -", u)
103
+ usage = data.get("usage", {})
104
+ print(f"\n[gear=deep model={model} total_tokens={usage.get('total_tokens')}]", file=sys.stderr)
105
+
106
+
107
+ def main() -> None:
108
+ args = sys.argv[1:]
109
+ deep = False
110
+ model = MODELS["luna"]
111
+ if "--deep" in args:
112
+ deep = True
113
+ args.remove("--deep")
114
+ if "--model" in args:
115
+ i = args.index("--model")
116
+ try:
117
+ model = MODELS[args[i + 1]]
118
+ except (IndexError, KeyError):
119
+ print("--model must be 'luna' or 'sol'", file=sys.stderr)
120
+ sys.exit(2)
121
+ del args[i : i + 2]
122
+ if not deep:
123
+ print("--model only applies with --deep", file=sys.stderr)
124
+ sys.exit(2)
125
+ if not args:
126
+ print('usage: search.py [--deep] "<question>" [--model sol|luna]', file=sys.stderr)
127
+ sys.exit(2)
128
+ question = " ".join(args)
129
+
130
+ if deep:
131
+ deep_search(question, model)
132
+ else:
133
+ fast_search(question)
134
+
135
+
136
+ if __name__ == "__main__":
137
+ main()
@@ -53,7 +53,7 @@ Agents (2):
53
53
  Skills (3):
54
54
  - repo-conventions (workspace)
55
55
  - aws-knowledge (global)
56
- - nova-web-search (global)
56
+ - web-search (global)
57
57
 
58
58
  Instructions: .halo/INSTRUCTIONS.md (global fallback)
59
59
  INDEX.md → 2 docs followed: docs/architecture/auth.md, docs/dev/api.md
@@ -74,7 +74,7 @@ BUILTIN_AGENT_IDS = {
74
74
  }
75
75
  BUILTIN_SKILL_IDS = {
76
76
  "agent", "skill", "workspace", "cron", "send-file", "acp",
77
- "aws-knowledge", "nova-web-search", "self",
77
+ "aws-knowledge", "web-search", "self",
78
78
  }
79
79
 
80
80
  # --- helpers ---------------------------------------------------------------
@@ -1,65 +0,0 @@
1
- ---
2
- name: nova-web-search
3
- description: Real-time web search using Amazon Nova 2 Lite's built-in nova_grounding system tool. Use this skill when the user asks about current events, news, latest updates, prices, or any real-time information. This replaces the web_search tool.
4
- user-invocable: false
5
- ---
6
- # Nova Web Search
7
-
8
- Perform real-time web search using Amazon Nova 2 Lite's built-in `nova_grounding` system tool. Returns a synthesized answer with source URLs.
9
-
10
- ## How to Use
11
-
12
- Run the following Python script via `shell_exec`:
13
-
14
- ```python
15
- import boto3
16
- from botocore.config import Config
17
-
18
- bedrock = boto3.client("bedrock-runtime", region_name="us-east-1", config=Config(read_timeout=60))
19
-
20
- query = "YOUR_QUERY_HERE" # Replace with the actual search query
21
-
22
- response = bedrock.converse(
23
- modelId="us.amazon.nova-2-lite-v1:0",
24
- messages=[{"role": "user", "content": [{"text": query}]}],
25
- toolConfig={"tools": [{"systemTool": {"name": "nova_grounding"}}]}
26
- )
27
-
28
- answer = ""
29
- sources = []
30
- for content in response["output"]["message"]["content"]:
31
- if "text" in content:
32
- answer += content["text"]
33
- elif "citationsContent" in content:
34
- for c in content["citationsContent"].get("citations", []):
35
- url = c.get("location", {}).get("web", {}).get("url", "")
36
- if url and url not in sources:
37
- sources.append(url)
38
-
39
- print(answer)
40
- if sources:
41
- print("\nSources:", sources)
42
- ```
43
-
44
- ## Query Formulation
45
-
46
- Nova grounds better against full natural-language questions than against
47
- keyword stubs — raw keywords return more loosely-related sources, while
48
- a question gives Nova enough context to filter.
49
-
50
- | User Intent | Query Template |
51
- |---|---|
52
- | News / updates / latest developments | `"What are the latest news about X?"` |
53
- | Product / specs / concept lookup | `"Tell me about X, including specs, price, and latest updates."` |
54
- | General search | `"Search for information about X."` |
55
-
56
- **Examples:**
57
- - ✅ `"What are the latest news about the Middle East conflict?"`
58
- - ✅ `"Tell me about the NVIDIA RTX 5080, including specs and price."`
59
- - ✗ `"RTX 5080"` — terse keywords leave Nova guessing at intent
60
-
61
- ## Requirements
62
-
63
- - Region **must** be `us-east-1`
64
- - English queries yield better results than other languages
65
- - Model ID: `us.amazon.nova-2-lite-v1:0`