@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/admin-out/404.html +1 -1
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +1 -1
- package/bundled-docs/design/cron.md +1 -1
- package/bundled-docs/design/goal-mode.md +1 -0
- package/bundled-docs/design/session.md +3 -0
- package/dist/index.js +508 -458
- package/package.json +1 -1
- package/templates/agents/default/agent.yaml +1 -1
- package/templates/models/aws-bedrock-claude-invoke.yaml +28 -0
- package/templates/skills/skill/SKILL.md +1 -1
- package/templates/skills/web-search/SKILL.md +44 -0
- package/templates/skills/web-search/search.py +137 -0
- package/templates/skills/workspace/share.md +1 -1
- package/templates/skills/workspace/stage.py +1 -1
- package/templates/skills/nova-web-search/SKILL.md +0 -65
- /package/admin-out/_next/static/{oDvBPFdNAW2vHdHcxQ0lw → 1KUHYpULbJGJOCucGcX9D}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{oDvBPFdNAW2vHdHcxQ0lw → 1KUHYpULbJGJOCucGcX9D}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -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`, `
|
|
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
|
-
-
|
|
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", "
|
|
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`
|
/package/admin-out/_next/static/{oDvBPFdNAW2vHdHcxQ0lw → 1KUHYpULbJGJOCucGcX9D}/_buildManifest.js
RENAMED
|
File without changes
|
/package/admin-out/_next/static/{oDvBPFdNAW2vHdHcxQ0lw → 1KUHYpULbJGJOCucGcX9D}/_ssgManifest.js
RENAMED
|
File without changes
|