@chatpanel/events 0.69.0 → 0.69.1

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/find-tool.js CHANGED
@@ -34,11 +34,18 @@ export const FIND_DESCRIPTION =
34
34
  // meeting history" — while `find` was sitting in its toolset. The old line named the tool
35
35
  // and left the capability to be inferred, and inference is what small models are worst at.
36
36
  export const FIND_RESIDENT =
37
- "You HAVE access to the user's own ChatPanel data — their past chats, notes, and "
38
- + 'meeting transcripts and summaries — through the `find` tool, plus the web. When the '
39
- + 'question is about past meetings, notes, people, decisions, or anything the user '
40
- + 'discussed or wrote, call `find` FIRST and answer from what it returns. Never tell '
41
- + 'the user you cannot access their meetings, notes or history: you can.';
37
+ "You HAVE access to the user's own ChatPanel data — past chats, notes, meeting "
38
+ + 'transcripts and summaries — through `find`, plus the web. For anything they discussed or '
39
+ + 'wrote, call `find` FIRST and answer from it; never say you cannot access their meetings, '
40
+ + 'notes or history. '
41
+ // Named here, resident, because a relayed agent with a web search of its own otherwise
42
+ // reaches for that: the desktop asked Codex about the weather, Codex searched on its own,
43
+ // read pages that were scripts and no temperature, and answered that it could not tell.
44
+ // The `weather` action answers in one request. And a `find` call is shown to the user as
45
+ // a step — an agent's own search is not. The whole block stays under the extension's
46
+ // 120-token resident cap (test-data-dispatch.mjs): the manual travels with `describe`.
47
+ + 'For anything current — weather, prices, news — use `find` (actions `weather`, '
48
+ + '`web_search`), not a search tool of your own; the user sees `find` calls as steps.';
42
49
 
43
50
  /**
44
51
  * Wrap the real search/read tools (history, web search, weather…) as the one `find` tool.
package/index.js CHANGED
@@ -283,3 +283,4 @@ export { McpClient, mcpProvider } from './mcp-client.js';
283
283
  export { mcpSharedSystem, mcpInventorySystem, sourceCitationSystem, combineSystemPrompt, toolStatus, widgetAuthoringSystem, vaultWidgetSystem, wantsVaultGuidance } from './tool-hints.js';
284
284
  export { adaptiveToolRetryHint, createAdaptiveToolPolicy, isInvalidToolParametersResult } from './adaptive-tool-policy.js';
285
285
  export { getMcpProviders, testMcpServer, resetMcp } from './mcp-manager.js';
286
+ export { WEATHER_TOOL_NAME, WEATHER_TOOL_SYSTEM, weatherToolProvider } from './weather-tool.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatpanel/events",
3
- "version": "0.69.0",
3
+ "version": "0.69.1",
4
4
  "description": "The canonical ChatPanel event-log and capability contracts \u2014 typed durable facts, clock-free deterministic linearization, schema upcasting, and the invariants the replay harness asserts. Pure, dependency-free ESM shared by the ChatPanel extension, gateway and bridge.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -105,7 +105,8 @@
105
105
  "./tool-hints.js": "./tool-hints.js",
106
106
  "./adaptive-tool-policy.js": "./adaptive-tool-policy.js",
107
107
  "./mcp-client.js": "./mcp-client.js",
108
- "./mcp-manager.js": "./mcp-manager.js"
108
+ "./mcp-manager.js": "./mcp-manager.js",
109
+ "./weather-tool.js": "./weather-tool.js"
109
110
  },
110
111
  "files": [
111
112
  "LICENSE",
@@ -203,6 +204,7 @@
203
204
  "vault.js",
204
205
  "view.js",
205
206
  "voice-intents.js",
207
+ "weather-tool.js",
206
208
  "weather.js",
207
209
  "web-search-tool.js",
208
210
  "web-search.js",
@@ -0,0 +1,65 @@
1
+ // `weather` as a TOOL — the spec, the guidance, and the answer shape, without the fetch.
2
+ //
3
+ // The extension answers "how is the weather in X" with this rather than with a web search,
4
+ // because weather sites are rendered by scripts and a fetched page is navigation and no
5
+ // temperature — which is exactly what a desktop turn without this tool produced: "I couldn't
6
+ // retrieve reliable live weather". The engine (`weather.js`: the wttr.in query, the parse,
7
+ // the ambiguity check) is already shared; this is the tool every client arms in front of it.
8
+ //
9
+ // `fetchJson(url, { timeoutMs })` is injected: each host owns its network guard and applies
10
+ // it to a URL a MODEL supplied — attacker-influenced by construction.
11
+
12
+ import { getWeather } from './weather.js';
13
+
14
+ export const WEATHER_TOOL_NAME = 'weather';
15
+
16
+ export const WEATHER_TOOL_SYSTEM =
17
+ 'For weather, call `weather` FIRST — it answers the whole question in one request. Only '
18
+ + 'fall back to web_search if it tells you to. Report the location it says it resolved, '
19
+ + 'because a bare town name can geocode to the wrong place.';
20
+
21
+ export function weatherToolProvider({ fetchJson } = {}) {
22
+ if (typeof fetchJson !== 'function') throw new Error('weatherToolProvider: fetchJson required');
23
+ return {
24
+ specs: [
25
+ {
26
+ name: 'weather',
27
+ // A read, declared: the round runner overlaps reads and serialises everything it
28
+ // cannot classify, and "weather" is not a verb its name heuristic knows.
29
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
30
+ description:
31
+ 'Current conditions and a short forecast for one place, in a single request. Use this '
32
+ + 'for any weather question instead of searching. Returns the location it actually '
33
+ + 'resolved to — say which place the answer is for.',
34
+ parameters: {
35
+ type: 'object',
36
+ properties: {
37
+ location: {
38
+ type: 'string',
39
+ description: 'A place, as the user said it. Add a state or country only if THEY did '
40
+ + '— "Fairview, OR" if they said so, plain "Fairview" if they did not.',
41
+ },
42
+ },
43
+ required: ['location'],
44
+ additionalProperties: false,
45
+ },
46
+ },
47
+ ],
48
+ system: WEATHER_TOOL_SYSTEM,
49
+ async execute(name, input) {
50
+ if (name !== 'weather') return JSON.stringify({ error: `Unknown tool: ${name}` });
51
+ const location = String(input?.location || '').trim();
52
+ if (!location) return 'No location provided to weather.';
53
+ const got = await getWeather(location, { fetchJson });
54
+ // THE FALLBACK IS AN INSTRUCTION, not an error string. A model handed "weather failed"
55
+ // stops, or apologises; a model told which tool answers this next just uses it. The
56
+ // whole point of preferring one source is that it must degrade to the general one.
57
+ if (!got.ok) {
58
+ return `The weather service could not answer for "${location}" (${got.reason}). `
59
+ + `Now call web_search for "weather in ${location}" and answer from the results — `
60
+ + 'do not tell the user a tool failed.';
61
+ }
62
+ return { text: got.text, note: 'ChatPanel · wttr.in' };
63
+ },
64
+ };
65
+ }