@vellumai/assistant 0.4.30 → 0.4.32
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/ARCHITECTURE.md +1 -1
- package/Dockerfile +14 -8
- package/README.md +2 -2
- package/docs/architecture/memory.md +28 -29
- package/docs/runbook-trusted-contacts.md +1 -4
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -7
- package/src/__tests__/anthropic-provider.test.ts +86 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/checker.test.ts +37 -98
- package/src/__tests__/commit-message-enrichment-service.test.ts +15 -4
- package/src/__tests__/config-schema.test.ts +6 -14
- package/src/__tests__/conflict-policy.test.ts +76 -0
- package/src/__tests__/conflict-store.test.ts +14 -20
- package/src/__tests__/contacts-tools.test.ts +8 -61
- package/src/__tests__/contradiction-checker.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/daemon-server-session-init.test.ts +1 -19
- package/src/__tests__/followup-tools.test.ts +0 -30
- package/src/__tests__/gemini-provider.test.ts +79 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-routing-invariants.test.ts +6 -4
- package/src/__tests__/ipc-snapshot.test.ts +0 -4
- package/src/__tests__/managed-proxy-context.test.ts +163 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +13 -12
- package/src/__tests__/memory-regressions.test.ts +6 -6
- package/src/__tests__/openai-provider.test.ts +82 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +134 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +269 -0
- package/src/__tests__/recurrence-types.test.ts +0 -15
- package/src/__tests__/registry.test.ts +0 -10
- package/src/__tests__/schedule-tools.test.ts +28 -44
- package/src/__tests__/script-proxy-session-runtime.test.ts +6 -1
- package/src/__tests__/session-agent-loop.test.ts +0 -2
- package/src/__tests__/session-conflict-gate.test.ts +243 -388
- package/src/__tests__/session-profile-injection.test.ts +0 -2
- package/src/__tests__/session-runtime-assembly.test.ts +2 -3
- package/src/__tests__/session-skill-tools.test.ts +0 -49
- package/src/__tests__/session-workspace-cache-state.test.ts +0 -1
- package/src/__tests__/session-workspace-injection.test.ts +0 -1
- package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/task-management-tools.test.ts +111 -0
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -1
- package/src/__tests__/twilio-config.test.ts +0 -3
- package/src/amazon/session.ts +30 -91
- package/src/approvals/guardian-decision-primitive.ts +11 -7
- package/src/approvals/guardian-request-resolvers.ts +5 -3
- package/src/calls/call-controller.ts +423 -571
- package/src/calls/finalize-call.ts +20 -0
- package/src/calls/relay-access-wait.ts +340 -0
- package/src/calls/relay-server.ts +269 -899
- package/src/calls/relay-setup-router.ts +307 -0
- package/src/calls/relay-verification.ts +280 -0
- package/src/calls/twilio-config.ts +1 -8
- package/src/calls/voice-control-protocol.ts +184 -0
- package/src/calls/voice-session-bridge.ts +1 -8
- package/src/config/agent-schema.ts +1 -1
- package/src/config/bundled-skills/contacts/SKILL.md +7 -18
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -20
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +2 -4
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +6 -12
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +3 -24
- package/src/config/bundled-skills/followups/TOOLS.json +0 -4
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -10
- package/src/config/bundled-tool-registry.ts +0 -5
- package/src/config/core-schema.ts +1 -1
- package/src/config/env.ts +0 -10
- package/src/config/feature-flag-registry.json +1 -1
- package/src/config/loader.ts +19 -0
- package/src/config/memory-schema.ts +0 -10
- package/src/config/schema.ts +2 -2
- package/src/config/system-prompt.ts +6 -0
- package/src/contacts/contact-store.ts +36 -62
- package/src/contacts/contacts-write.ts +14 -3
- package/src/contacts/types.ts +9 -4
- package/src/daemon/handlers/config-heartbeat.ts +1 -2
- package/src/daemon/handlers/contacts.ts +2 -2
- package/src/daemon/handlers/guardian-actions.ts +1 -1
- package/src/daemon/handlers/session-history.ts +398 -0
- package/src/daemon/handlers/session-user-message.ts +982 -0
- package/src/daemon/handlers/sessions.ts +9 -1337
- package/src/daemon/ipc-contract/contacts.ts +2 -2
- package/src/daemon/ipc-contract/sessions.ts +0 -6
- package/src/daemon/ipc-contract-inventory.json +0 -1
- package/src/daemon/lifecycle.ts +0 -29
- package/src/daemon/session-agent-loop.ts +1 -45
- package/src/daemon/session-conflict-gate.ts +21 -82
- package/src/daemon/session-memory.ts +7 -52
- package/src/daemon/session-process.ts +3 -1
- package/src/daemon/session-runtime-assembly.ts +18 -35
- package/src/heartbeat/heartbeat-service.ts +5 -1
- package/src/home-base/app-link-store.ts +0 -7
- package/src/memory/conflict-intent.ts +3 -6
- package/src/memory/conflict-policy.ts +34 -0
- package/src/memory/conflict-store.ts +10 -18
- package/src/memory/contradiction-checker.ts +2 -2
- package/src/memory/conversation-attention-store.ts +1 -1
- package/src/memory/conversation-store.ts +0 -51
- package/src/memory/db-init.ts +8 -0
- package/src/memory/job-handlers/conflict.ts +24 -7
- package/src/memory/migrations/105-contacts-and-triage.ts +4 -7
- package/src/memory/migrations/134-contacts-notes-column.ts +68 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +31 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/recall-cache.ts +0 -5
- package/src/memory/schema/calls.ts +274 -0
- package/src/memory/schema/contacts.ts +125 -0
- package/src/memory/schema/conversations.ts +129 -0
- package/src/memory/schema/guardian.ts +172 -0
- package/src/memory/schema/index.ts +8 -0
- package/src/memory/schema/infrastructure.ts +205 -0
- package/src/memory/schema/memory-core.ts +196 -0
- package/src/memory/schema/notifications.ts +191 -0
- package/src/memory/schema/tasks.ts +78 -0
- package/src/memory/schema.ts +1 -1402
- package/src/memory/slack-thread-store.ts +0 -69
- package/src/messaging/index.ts +0 -1
- package/src/messaging/types.ts +0 -38
- package/src/notifications/decisions-store.ts +2 -105
- package/src/notifications/deliveries-store.ts +0 -11
- package/src/notifications/preferences-store.ts +1 -58
- package/src/permissions/checker.ts +6 -17
- package/src/providers/anthropic/client.ts +6 -2
- package/src/providers/gemini/client.ts +13 -2
- package/src/providers/managed-proxy/constants.ts +55 -0
- package/src/providers/managed-proxy/context.ts +77 -0
- package/src/providers/registry.ts +112 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +51 -23
- package/src/runtime/guardian-action-service.ts +3 -2
- package/src/runtime/guardian-outbound-actions.ts +3 -3
- package/src/runtime/guardian-reply-router.ts +4 -4
- package/src/runtime/http-server.ts +83 -710
- package/src/runtime/http-types.ts +0 -16
- package/src/runtime/middleware/auth.ts +0 -12
- package/src/runtime/routes/app-routes.ts +33 -0
- package/src/runtime/routes/approval-routes.ts +32 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +6 -3
- package/src/runtime/routes/attachment-routes.ts +32 -0
- package/src/runtime/routes/brain-graph-routes.ts +27 -0
- package/src/runtime/routes/call-routes.ts +41 -0
- package/src/runtime/routes/channel-readiness-routes.ts +20 -0
- package/src/runtime/routes/channel-routes.ts +70 -0
- package/src/runtime/routes/contact-routes.ts +371 -29
- package/src/runtime/routes/conversation-attention-routes.ts +15 -0
- package/src/runtime/routes/conversation-routes.ts +192 -194
- package/src/runtime/routes/debug-routes.ts +15 -0
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/global-search-routes.ts +17 -2
- package/src/runtime/routes/guardian-action-routes.ts +23 -1
- package/src/runtime/routes/guardian-approval-interception.ts +2 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +26 -1
- package/src/runtime/routes/guardian-refresh-routes.ts +20 -0
- package/src/runtime/routes/identity-routes.ts +20 -0
- package/src/runtime/routes/inbound-message-handler.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +5 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +83 -0
- package/src/runtime/routes/invite-routes.ts +31 -0
- package/src/runtime/routes/migration-routes.ts +47 -17
- package/src/runtime/routes/pairing-routes.ts +18 -0
- package/src/runtime/routes/secret-routes.ts +20 -0
- package/src/runtime/routes/surface-action-routes.ts +26 -0
- package/src/runtime/routes/trust-rules-routes.ts +31 -0
- package/src/runtime/routes/twilio-routes.ts +79 -0
- package/src/schedule/recurrence-types.ts +1 -11
- package/src/tools/followups/followup_create.ts +9 -3
- package/src/tools/mcp/mcp-tool-factory.ts +0 -17
- package/src/tools/memory/definitions.ts +0 -6
- package/src/tools/network/script-proxy/session-manager.ts +38 -3
- package/src/tools/schedule/create.ts +1 -3
- package/src/tools/schedule/update.ts +9 -6
- package/src/twitter/session.ts +29 -77
- package/src/util/cookie-session.ts +114 -0
- package/src/workspace/git-service.ts +6 -4
- package/src/__tests__/conversation-routes.test.ts +0 -99
- package/src/__tests__/get-weather.test.ts +0 -393
- package/src/__tests__/task-tools.test.ts +0 -685
- package/src/__tests__/weather-skill-regression.test.ts +0 -276
- package/src/autonomy/autonomy-resolver.ts +0 -62
- package/src/autonomy/autonomy-store.ts +0 -138
- package/src/autonomy/disposition-mapper.ts +0 -31
- package/src/autonomy/index.ts +0 -11
- package/src/autonomy/types.ts +0 -43
- package/src/config/bundled-skills/weather/SKILL.md +0 -38
- package/src/config/bundled-skills/weather/TOOLS.json +0 -36
- package/src/config/bundled-skills/weather/icon.svg +0 -24
- package/src/config/bundled-skills/weather/tools/get-weather.ts +0 -12
- package/src/contacts/startup-migration.ts +0 -21
- package/src/messaging/triage-engine.ts +0 -344
- package/src/tools/weather/service.ts +0 -712
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
import { afterAll, describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { readFileSync } from "fs";
|
|
4
|
-
import { dirname, join } from "path";
|
|
5
|
-
|
|
6
|
-
import { __resetRegistryForTesting, getTool } from "../tools/registry.js";
|
|
7
|
-
import type { ToolContext } from "../tools/types.js";
|
|
8
|
-
import {
|
|
9
|
-
weatherCodeToDescription,
|
|
10
|
-
weatherCodeToSFSymbol,
|
|
11
|
-
} from "../tools/weather/service.js";
|
|
12
|
-
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Regression tests: ensure the skill-loaded path produces the same results
|
|
15
|
-
// as the legacy hardcoded path after the weather tool migration.
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
|
|
18
|
-
// Clean up after this file to prevent contamination of later test files.
|
|
19
|
-
afterAll(() => {
|
|
20
|
-
__resetRegistryForTesting();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const CONFIG_DIR = join(
|
|
24
|
-
dirname(import.meta.dirname!),
|
|
25
|
-
"config",
|
|
26
|
-
"bundled-skills",
|
|
27
|
-
"weather",
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
describe("weather skill script wrapper", () => {
|
|
31
|
-
test("exports a run function without registering get_weather in the tool registry", async () => {
|
|
32
|
-
// Before importing the wrapper, verify get_weather is not in the registry.
|
|
33
|
-
expect(getTool("get_weather")).toBeUndefined();
|
|
34
|
-
|
|
35
|
-
// Dynamic import of the skill wrapper — it should NOT trigger any
|
|
36
|
-
// registerTool side-effect (the wrapper delegates to the service module).
|
|
37
|
-
const mod =
|
|
38
|
-
await import("../config/bundled-skills/weather/tools/get-weather.js");
|
|
39
|
-
expect(typeof mod.run).toBe("function");
|
|
40
|
-
|
|
41
|
-
// After importing, the registry should still be clean — no side-effect.
|
|
42
|
-
expect(getTool("get_weather")).toBeUndefined();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("run function delegates to executeGetWeather from the service module", async () => {
|
|
46
|
-
const mod =
|
|
47
|
-
await import("../config/bundled-skills/weather/tools/get-weather.js");
|
|
48
|
-
|
|
49
|
-
// Provide a minimal mock fetch and minimal input to verify delegation.
|
|
50
|
-
const mockFetch = (async (url: string | URL | Request) => {
|
|
51
|
-
const urlStr =
|
|
52
|
-
typeof url === "string"
|
|
53
|
-
? url
|
|
54
|
-
: url instanceof URL
|
|
55
|
-
? url.href
|
|
56
|
-
: (url as Request).url;
|
|
57
|
-
|
|
58
|
-
if (urlStr.includes("geocoding-api.open-meteo.com")) {
|
|
59
|
-
return new Response(
|
|
60
|
-
JSON.stringify({
|
|
61
|
-
results: [
|
|
62
|
-
{
|
|
63
|
-
name: "TestCity",
|
|
64
|
-
latitude: 0,
|
|
65
|
-
longitude: 0,
|
|
66
|
-
country: "Testland",
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
}),
|
|
70
|
-
{ status: 200, headers: { "content-type": "application/json" } },
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (urlStr.includes("api.open-meteo.com")) {
|
|
75
|
-
return new Response(
|
|
76
|
-
JSON.stringify({
|
|
77
|
-
current: {
|
|
78
|
-
time: "2025-01-15T08:00",
|
|
79
|
-
temperature_2m: 20,
|
|
80
|
-
relative_humidity_2m: 50,
|
|
81
|
-
apparent_temperature: 19,
|
|
82
|
-
weather_code: 0,
|
|
83
|
-
wind_speed_10m: 10,
|
|
84
|
-
wind_direction_10m: 180,
|
|
85
|
-
},
|
|
86
|
-
current_units: {
|
|
87
|
-
temperature_2m: "°C",
|
|
88
|
-
relative_humidity_2m: "%",
|
|
89
|
-
apparent_temperature: "°C",
|
|
90
|
-
wind_speed_10m: "km/h",
|
|
91
|
-
wind_direction_10m: "°",
|
|
92
|
-
},
|
|
93
|
-
hourly: {
|
|
94
|
-
time: [],
|
|
95
|
-
temperature_2m: [],
|
|
96
|
-
weather_code: [],
|
|
97
|
-
is_day: [],
|
|
98
|
-
},
|
|
99
|
-
hourly_units: {
|
|
100
|
-
temperature_2m: "°C",
|
|
101
|
-
weather_code: "wmo code",
|
|
102
|
-
is_day: "",
|
|
103
|
-
},
|
|
104
|
-
daily: {
|
|
105
|
-
time: [],
|
|
106
|
-
weather_code: [],
|
|
107
|
-
temperature_2m_max: [],
|
|
108
|
-
temperature_2m_min: [],
|
|
109
|
-
precipitation_probability_max: [],
|
|
110
|
-
},
|
|
111
|
-
daily_units: {
|
|
112
|
-
temperature_2m_max: "°C",
|
|
113
|
-
temperature_2m_min: "°C",
|
|
114
|
-
precipitation_probability_max: "%",
|
|
115
|
-
},
|
|
116
|
-
}),
|
|
117
|
-
{ status: 200, headers: { "content-type": "application/json" } },
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return new Response("Not found", { status: 404 });
|
|
122
|
-
}) as typeof globalThis.fetch;
|
|
123
|
-
|
|
124
|
-
// Temporarily replace globalThis.fetch so the wrapper picks it up
|
|
125
|
-
const originalFetch = globalThis.fetch;
|
|
126
|
-
globalThis.fetch = mockFetch;
|
|
127
|
-
try {
|
|
128
|
-
const result = await mod.run({ location: "TestCity" }, {
|
|
129
|
-
proxyToolResolver: undefined,
|
|
130
|
-
} as unknown as ToolContext);
|
|
131
|
-
expect(result.isError).toBe(false);
|
|
132
|
-
expect(result.content).toContain("TestCity");
|
|
133
|
-
expect(result.content).toContain("Clear sky");
|
|
134
|
-
} finally {
|
|
135
|
-
globalThis.fetch = originalFetch;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe("weather TOOLS.json manifest", () => {
|
|
141
|
-
const manifest = JSON.parse(
|
|
142
|
-
readFileSync(join(CONFIG_DIR, "TOOLS.json"), "utf-8"),
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
test("has version 1", () => {
|
|
146
|
-
expect(manifest.version).toBe(1);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test("declares exactly one tool", () => {
|
|
150
|
-
expect(manifest.tools).toHaveLength(1);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("tool is named get_weather", () => {
|
|
154
|
-
expect(manifest.tools[0].name).toBe("get_weather");
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("tool has correct description", () => {
|
|
158
|
-
expect(manifest.tools[0].description).toBe(
|
|
159
|
-
"Get current weather conditions and forecast for a location",
|
|
160
|
-
);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("tool executor points to the skill script wrapper", () => {
|
|
164
|
-
expect(manifest.tools[0].executor).toBe("tools/get-weather.ts");
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("tool execution_target is host", () => {
|
|
168
|
-
expect(manifest.tools[0].execution_target).toBe("host");
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("input schema matches the legacy tool definition", () => {
|
|
172
|
-
const schema = manifest.tools[0].input_schema;
|
|
173
|
-
expect(schema.type).toBe("object");
|
|
174
|
-
expect(schema.required).toEqual(["location"]);
|
|
175
|
-
|
|
176
|
-
// location property
|
|
177
|
-
expect(schema.properties.location).toBeDefined();
|
|
178
|
-
expect(schema.properties.location.type).toBe("string");
|
|
179
|
-
|
|
180
|
-
// units property
|
|
181
|
-
expect(schema.properties.units).toBeDefined();
|
|
182
|
-
expect(schema.properties.units.type).toBe("string");
|
|
183
|
-
expect(schema.properties.units.enum).toEqual(["celsius", "fahrenheit"]);
|
|
184
|
-
|
|
185
|
-
// days property
|
|
186
|
-
expect(schema.properties.days).toBeDefined();
|
|
187
|
-
expect(schema.properties.days.type).toBe("number");
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
describe("weather service module isolation", () => {
|
|
192
|
-
test("executeGetWeather is importable without registerTool side effects", async () => {
|
|
193
|
-
// Importing the service module should NOT call registerTool — only the
|
|
194
|
-
// legacy get-weather.ts module does that.
|
|
195
|
-
const mod = await import("../tools/weather/service.js");
|
|
196
|
-
expect(typeof mod.executeGetWeather).toBe("function");
|
|
197
|
-
expect(typeof mod.weatherCodeToDescription).toBe("function");
|
|
198
|
-
expect(typeof mod.weatherCodeToSFSymbol).toBe("function");
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test("weatherCodeToDescription returns correct values for all major code families", () => {
|
|
202
|
-
// Clear/cloudy family
|
|
203
|
-
expect(weatherCodeToDescription(0)).toBe("Clear sky");
|
|
204
|
-
expect(weatherCodeToDescription(1)).toBe("Mainly clear");
|
|
205
|
-
expect(weatherCodeToDescription(2)).toBe("Partly cloudy");
|
|
206
|
-
expect(weatherCodeToDescription(3)).toBe("Overcast");
|
|
207
|
-
|
|
208
|
-
// Fog family
|
|
209
|
-
expect(weatherCodeToDescription(45)).toBe("Foggy");
|
|
210
|
-
expect(weatherCodeToDescription(48)).toBe("Depositing rime fog");
|
|
211
|
-
|
|
212
|
-
// Drizzle family
|
|
213
|
-
expect(weatherCodeToDescription(51)).toBe("Light drizzle");
|
|
214
|
-
expect(weatherCodeToDescription(53)).toBe("Moderate drizzle");
|
|
215
|
-
expect(weatherCodeToDescription(55)).toBe("Dense drizzle");
|
|
216
|
-
|
|
217
|
-
// Freezing drizzle
|
|
218
|
-
expect(weatherCodeToDescription(56)).toBe("Light freezing drizzle");
|
|
219
|
-
expect(weatherCodeToDescription(57)).toBe("Dense freezing drizzle");
|
|
220
|
-
|
|
221
|
-
// Rain family
|
|
222
|
-
expect(weatherCodeToDescription(61)).toBe("Slight rain");
|
|
223
|
-
expect(weatherCodeToDescription(63)).toBe("Moderate rain");
|
|
224
|
-
expect(weatherCodeToDescription(65)).toBe("Heavy rain");
|
|
225
|
-
|
|
226
|
-
// Freezing rain
|
|
227
|
-
expect(weatherCodeToDescription(66)).toBe("Light freezing rain");
|
|
228
|
-
expect(weatherCodeToDescription(67)).toBe("Heavy freezing rain");
|
|
229
|
-
|
|
230
|
-
// Snow family
|
|
231
|
-
expect(weatherCodeToDescription(71)).toBe("Slight snowfall");
|
|
232
|
-
expect(weatherCodeToDescription(73)).toBe("Moderate snowfall");
|
|
233
|
-
expect(weatherCodeToDescription(75)).toBe("Heavy snowfall");
|
|
234
|
-
expect(weatherCodeToDescription(77)).toBe("Snow grains");
|
|
235
|
-
|
|
236
|
-
// Shower family
|
|
237
|
-
expect(weatherCodeToDescription(80)).toBe("Slight rain showers");
|
|
238
|
-
expect(weatherCodeToDescription(81)).toBe("Moderate rain showers");
|
|
239
|
-
expect(weatherCodeToDescription(82)).toBe("Violent rain showers");
|
|
240
|
-
expect(weatherCodeToDescription(85)).toBe("Slight snow showers");
|
|
241
|
-
expect(weatherCodeToDescription(86)).toBe("Heavy snow showers");
|
|
242
|
-
|
|
243
|
-
// Thunderstorm family
|
|
244
|
-
expect(weatherCodeToDescription(95)).toBe("Thunderstorm");
|
|
245
|
-
expect(weatherCodeToDescription(96)).toBe("Thunderstorm with slight hail");
|
|
246
|
-
expect(weatherCodeToDescription(99)).toBe("Thunderstorm with heavy hail");
|
|
247
|
-
|
|
248
|
-
// Unknown codes
|
|
249
|
-
expect(weatherCodeToDescription(-1)).toBe("Unknown");
|
|
250
|
-
expect(weatherCodeToDescription(42)).toBe("Unknown");
|
|
251
|
-
expect(weatherCodeToDescription(100)).toBe("Unknown");
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("weatherCodeToSFSymbol returns correct icons and respects isDay", () => {
|
|
255
|
-
// Day/night variants for clear sky
|
|
256
|
-
expect(weatherCodeToSFSymbol(0, true)).toBe("sun.max.fill");
|
|
257
|
-
expect(weatherCodeToSFSymbol(0, false)).toBe("moon.fill");
|
|
258
|
-
|
|
259
|
-
// Day/night variants for partly cloudy
|
|
260
|
-
expect(weatherCodeToSFSymbol(2, true)).toBe("cloud.sun.fill");
|
|
261
|
-
expect(weatherCodeToSFSymbol(2, false)).toBe("cloud.moon.fill");
|
|
262
|
-
|
|
263
|
-
// Overcast has no day/night variant
|
|
264
|
-
expect(weatherCodeToSFSymbol(3, true)).toBe("cloud.fill");
|
|
265
|
-
expect(weatherCodeToSFSymbol(3, false)).toBe("cloud.fill");
|
|
266
|
-
|
|
267
|
-
// Snow
|
|
268
|
-
expect(weatherCodeToSFSymbol(75, true)).toBe("snowflake");
|
|
269
|
-
|
|
270
|
-
// Thunderstorm
|
|
271
|
-
expect(weatherCodeToSFSymbol(95, true)).toBe("cloud.bolt.fill");
|
|
272
|
-
|
|
273
|
-
// Default isDay=true when omitted
|
|
274
|
-
expect(weatherCodeToSFSymbol(0)).toBe("sun.max.fill");
|
|
275
|
-
});
|
|
276
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Autonomy resolver — determines the effective autonomy tier for an
|
|
3
|
-
* inbound message based on its triage result, channel, and contact.
|
|
4
|
-
*
|
|
5
|
-
* Resolution order (first match wins):
|
|
6
|
-
* 1. Matched playbook with an explicit autonomy level
|
|
7
|
-
* 2. Contact-specific override
|
|
8
|
-
* 3. Category override for the triage result's category
|
|
9
|
-
* 4. Channel default
|
|
10
|
-
* 5. Global default tier (falls back to 'notify')
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { TriageResult } from "../messaging/types.js";
|
|
14
|
-
import { getAutonomyConfig } from "./autonomy-store.js";
|
|
15
|
-
import type { AutonomyTier } from "./types.js";
|
|
16
|
-
import { AUTONOMY_TIERS } from "./types.js";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Resolve the autonomy tier for a triaged message.
|
|
20
|
-
*
|
|
21
|
-
* @param triageResult - Output from the triage engine
|
|
22
|
-
* @param channel - The channel the message arrived on (e.g. 'email', 'slack')
|
|
23
|
-
* @param contactId - Optional contact ID for contact-specific overrides
|
|
24
|
-
*/
|
|
25
|
-
export function resolveAutonomyTier(
|
|
26
|
-
triageResult: TriageResult,
|
|
27
|
-
channel: string,
|
|
28
|
-
contactId?: string,
|
|
29
|
-
): AutonomyTier {
|
|
30
|
-
// 1. Playbook-specified autonomy level (first matched playbook wins)
|
|
31
|
-
for (const playbook of triageResult.matchedPlaybooks) {
|
|
32
|
-
if (isValidTier(playbook.autonomyLevel)) {
|
|
33
|
-
return playbook.autonomyLevel as AutonomyTier;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const config = getAutonomyConfig();
|
|
38
|
-
|
|
39
|
-
// 2. Contact-specific override
|
|
40
|
-
if (contactId && Object.hasOwn(config.contactOverrides, contactId)) {
|
|
41
|
-
return config.contactOverrides[contactId];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// 3. Category override
|
|
45
|
-
if (Object.hasOwn(config.categoryOverrides, triageResult.category)) {
|
|
46
|
-
return config.categoryOverrides[triageResult.category];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// 4. Channel default
|
|
50
|
-
if (Object.hasOwn(config.channelDefaults, channel)) {
|
|
51
|
-
return config.channelDefaults[channel];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 5. Global default
|
|
55
|
-
return config.defaultTier;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function isValidTier(value: unknown): value is AutonomyTier {
|
|
59
|
-
return (
|
|
60
|
-
typeof value === "string" && AUTONOMY_TIERS.includes(value as AutonomyTier)
|
|
61
|
-
);
|
|
62
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Autonomy config persistence — reads/writes autonomy policy from a
|
|
3
|
-
* dedicated JSON file in the workspace directory.
|
|
4
|
-
*
|
|
5
|
-
* The autonomy config lives at ~/.vellum/workspace/autonomy.json, separate
|
|
6
|
-
* from the main config.json. This is policy configuration (not learned
|
|
7
|
-
* preferences), so it belongs on disk rather than in the SQLite database.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { dirname, join } from "node:path";
|
|
12
|
-
|
|
13
|
-
import { readTextFileSync } from "../util/fs.js";
|
|
14
|
-
import { getLogger } from "../util/logger.js";
|
|
15
|
-
import { getWorkspaceDir } from "../util/platform.js";
|
|
16
|
-
import type { AutonomyConfig, AutonomyTier } from "./types.js";
|
|
17
|
-
import { AUTONOMY_TIERS, DEFAULT_AUTONOMY_CONFIG } from "./types.js";
|
|
18
|
-
|
|
19
|
-
const log = getLogger("autonomy-store");
|
|
20
|
-
|
|
21
|
-
function getAutonomyConfigPath(): string {
|
|
22
|
-
return join(getWorkspaceDir(), "autonomy.json");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Load the current autonomy configuration from disk.
|
|
27
|
-
* Returns defaults if the file doesn't exist or is malformed.
|
|
28
|
-
*/
|
|
29
|
-
export function getAutonomyConfig(): AutonomyConfig {
|
|
30
|
-
const raw = readTextFileSync(getAutonomyConfigPath());
|
|
31
|
-
if (raw == null) {
|
|
32
|
-
return structuredClone(DEFAULT_AUTONOMY_CONFIG);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
return validateAutonomyConfig(JSON.parse(raw));
|
|
37
|
-
} catch (err) {
|
|
38
|
-
log.warn({ err }, "Failed to parse autonomy config; using defaults");
|
|
39
|
-
return structuredClone(DEFAULT_AUTONOMY_CONFIG);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Merge partial updates into the existing autonomy configuration and persist.
|
|
45
|
-
* Only the provided fields are updated; omitted fields keep their current values.
|
|
46
|
-
*/
|
|
47
|
-
export function setAutonomyConfig(
|
|
48
|
-
updates: Partial<AutonomyConfig>,
|
|
49
|
-
): AutonomyConfig {
|
|
50
|
-
const current = getAutonomyConfig();
|
|
51
|
-
|
|
52
|
-
if (updates.defaultTier !== undefined) {
|
|
53
|
-
current.defaultTier = updates.defaultTier;
|
|
54
|
-
}
|
|
55
|
-
if (updates.channelDefaults !== undefined) {
|
|
56
|
-
current.channelDefaults = {
|
|
57
|
-
...current.channelDefaults,
|
|
58
|
-
...updates.channelDefaults,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
if (updates.categoryOverrides !== undefined) {
|
|
62
|
-
current.categoryOverrides = {
|
|
63
|
-
...current.categoryOverrides,
|
|
64
|
-
...updates.categoryOverrides,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
if (updates.contactOverrides !== undefined) {
|
|
68
|
-
current.contactOverrides = {
|
|
69
|
-
...current.contactOverrides,
|
|
70
|
-
...updates.contactOverrides,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
persistConfig(current);
|
|
75
|
-
return current;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Get the autonomy tier configured for a specific channel.
|
|
80
|
-
* Returns undefined if no channel-specific default is set.
|
|
81
|
-
*/
|
|
82
|
-
export function getChannelDefault(channel: string): AutonomyTier | undefined {
|
|
83
|
-
const config = getAutonomyConfig();
|
|
84
|
-
return config.channelDefaults[channel];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Set the autonomy tier for a specific channel.
|
|
89
|
-
*/
|
|
90
|
-
export function setChannelDefault(channel: string, tier: AutonomyTier): void {
|
|
91
|
-
const config = getAutonomyConfig();
|
|
92
|
-
config.channelDefaults[channel] = tier;
|
|
93
|
-
persistConfig(config);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
// Internal helpers
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
|
|
100
|
-
function persistConfig(config: AutonomyConfig): void {
|
|
101
|
-
const configPath = getAutonomyConfigPath();
|
|
102
|
-
mkdirSync(dirname(configPath), { recursive: true });
|
|
103
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function isValidTier(value: unknown): value is AutonomyTier {
|
|
107
|
-
return (
|
|
108
|
-
typeof value === "string" && AUTONOMY_TIERS.includes(value as AutonomyTier)
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function validateTierRecord(raw: unknown): Record<string, AutonomyTier> {
|
|
113
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
114
|
-
const result: Record<string, AutonomyTier> = {};
|
|
115
|
-
for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {
|
|
116
|
-
if (isValidTier(value)) {
|
|
117
|
-
result[key] = value;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return result;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function validateAutonomyConfig(raw: unknown): AutonomyConfig {
|
|
124
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
125
|
-
return structuredClone(DEFAULT_AUTONOMY_CONFIG);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const obj = raw as Record<string, unknown>;
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
defaultTier: isValidTier(obj.defaultTier)
|
|
132
|
-
? obj.defaultTier
|
|
133
|
-
: DEFAULT_AUTONOMY_CONFIG.defaultTier,
|
|
134
|
-
channelDefaults: validateTierRecord(obj.channelDefaults),
|
|
135
|
-
categoryOverrides: validateTierRecord(obj.categoryOverrides),
|
|
136
|
-
contactOverrides: validateTierRecord(obj.contactOverrides),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Disposition mapper — bridges autonomy tiers to watcher event dispositions.
|
|
3
|
-
*
|
|
4
|
-
* The watcher engine uses three dispositions for event handling:
|
|
5
|
-
* - `silent` — act without notifying the user
|
|
6
|
-
* - `notify` — alert the user (with optional draft attached)
|
|
7
|
-
* - `escalate` — alert the user and take no autonomous action
|
|
8
|
-
*
|
|
9
|
-
* This module maps autonomy tiers to those dispositions:
|
|
10
|
-
* - `auto` → `silent` (act without notifying)
|
|
11
|
-
* - `draft` → `notify` (prepare draft, alert user for approval)
|
|
12
|
-
* - `notify` → `escalate` (alert user, take no action)
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import type { AutonomyTier } from "./types.js";
|
|
16
|
-
|
|
17
|
-
/** Watcher event disposition strings used by the watcher engine. */
|
|
18
|
-
export type WatcherDisposition = "silent" | "notify" | "escalate";
|
|
19
|
-
|
|
20
|
-
const TIER_TO_DISPOSITION: Record<AutonomyTier, WatcherDisposition> = {
|
|
21
|
-
auto: "silent",
|
|
22
|
-
draft: "notify",
|
|
23
|
-
notify: "escalate",
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Map an autonomy tier to the corresponding watcher disposition.
|
|
28
|
-
*/
|
|
29
|
-
export function mapTierToDisposition(tier: AutonomyTier): WatcherDisposition {
|
|
30
|
-
return TIER_TO_DISPOSITION[tier];
|
|
31
|
-
}
|
package/src/autonomy/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export { resolveAutonomyTier } from "./autonomy-resolver.js";
|
|
2
|
-
export {
|
|
3
|
-
getAutonomyConfig,
|
|
4
|
-
getChannelDefault,
|
|
5
|
-
setAutonomyConfig,
|
|
6
|
-
setChannelDefault,
|
|
7
|
-
} from "./autonomy-store.js";
|
|
8
|
-
export type { WatcherDisposition } from "./disposition-mapper.js";
|
|
9
|
-
export { mapTierToDisposition } from "./disposition-mapper.js";
|
|
10
|
-
export type { AutonomyConfig, AutonomyTier } from "./types.js";
|
|
11
|
-
export { AUTONOMY_TIERS, DEFAULT_AUTONOMY_CONFIG } from "./types.js";
|
package/src/autonomy/types.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Autonomy tier types — govern what runs unsupervised per channel/category/contact.
|
|
3
|
-
*
|
|
4
|
-
* Three tiers:
|
|
5
|
-
* - `auto` — act silently, no human in the loop
|
|
6
|
-
* - `draft` — prepare a draft for human approval before sending
|
|
7
|
-
* - `notify` — alert the user but take no action (most conservative)
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export type AutonomyTier = "auto" | "draft" | "notify";
|
|
11
|
-
|
|
12
|
-
export const AUTONOMY_TIERS: readonly AutonomyTier[] = [
|
|
13
|
-
"auto",
|
|
14
|
-
"draft",
|
|
15
|
-
"notify",
|
|
16
|
-
] as const;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Policy configuration for autonomy tiers. This is persisted as JSON config,
|
|
20
|
-
* not in the SQLite database — it represents explicit policy decisions, not
|
|
21
|
-
* learned preferences.
|
|
22
|
-
*/
|
|
23
|
-
export interface AutonomyConfig {
|
|
24
|
-
/** Global fallback tier when no more-specific rule matches. Defaults to 'notify'. */
|
|
25
|
-
defaultTier: AutonomyTier;
|
|
26
|
-
|
|
27
|
-
/** Per-channel defaults (e.g., { email: 'draft', slack: 'auto' }). */
|
|
28
|
-
channelDefaults: Record<string, AutonomyTier>;
|
|
29
|
-
|
|
30
|
-
/** Per-category overrides keyed by triage category (e.g., { newsletter: 'auto' }). */
|
|
31
|
-
categoryOverrides: Record<string, AutonomyTier>;
|
|
32
|
-
|
|
33
|
-
/** Per-contact overrides keyed by contact ID. */
|
|
34
|
-
contactOverrides: Record<string, AutonomyTier>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Sensible defaults — conservative: everything starts as notify-only. */
|
|
38
|
-
export const DEFAULT_AUTONOMY_CONFIG: AutonomyConfig = {
|
|
39
|
-
defaultTier: "notify",
|
|
40
|
-
channelDefaults: {},
|
|
41
|
-
categoryOverrides: {},
|
|
42
|
-
contactOverrides: {},
|
|
43
|
-
};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: "Weather"
|
|
3
|
-
description: "Get current weather conditions and forecasts for any location"
|
|
4
|
-
user-invocable: true
|
|
5
|
-
metadata: {"vellum": {"emoji": "\ud83c\udf24\ufe0f"}}
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
You are a weather assistant. When the user asks about weather, use the `get_weather` tool to fetch current conditions and forecasts for the requested location.
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
- **Current conditions**: "What's the weather in San Francisco?"
|
|
13
|
-
- **Multi-day forecast**: "Give me a 7-day forecast for Tokyo"
|
|
14
|
-
- **Specific units**: "Weather in London in celsius"
|
|
15
|
-
|
|
16
|
-
## Understanding the Output
|
|
17
|
-
|
|
18
|
-
The tool returns:
|
|
19
|
-
- **Current conditions** — temperature, feels-like temperature, humidity, wind speed and direction, and a description of conditions (e.g. "Partly cloudy")
|
|
20
|
-
- **Hourly forecast** — next 24 hours of temperature and conditions
|
|
21
|
-
- **Daily forecast** — high/low temperatures, precipitation probability, and conditions for each day
|
|
22
|
-
|
|
23
|
-
## Temperature Units
|
|
24
|
-
|
|
25
|
-
- Default unit is Fahrenheit. The user can request Celsius by saying "in celsius" or by specifying `units: "celsius"`.
|
|
26
|
-
- The rendered weather card includes a toggle to switch between units without re-fetching.
|
|
27
|
-
|
|
28
|
-
## Forecast Days
|
|
29
|
-
|
|
30
|
-
- Default is 10 days. The user can request anywhere from 1 to 16 days.
|
|
31
|
-
- Use fewer days when the user asks about "today" or "this weekend" — 1-3 days is sufficient.
|
|
32
|
-
- Use more days when the user asks for an extended or long-range forecast.
|
|
33
|
-
|
|
34
|
-
## Tips
|
|
35
|
-
|
|
36
|
-
- If the user provides an ambiguous location (e.g. "Springfield"), the geocoding API picks the most prominent match. If the result seems wrong, suggest the user be more specific (e.g. "Springfield, IL").
|
|
37
|
-
- The tool auto-renders a rich weather card with hourly and daily forecasts — you don't need to reformat the data as text unless the user explicitly asks for a text summary.
|
|
38
|
-
- The tool fetches **live data** from the Open-Meteo Weather API. Do NOT follow up with `web_search`, `ui_show`, or `ui_update` to verify, supplement, or re-render the data — the card is already accurate and complete. Just respond with a brief conversational summary.
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"tools": [
|
|
4
|
-
{
|
|
5
|
-
"name": "get_weather",
|
|
6
|
-
"description": "Get current weather conditions and forecast for a location",
|
|
7
|
-
"category": "weather",
|
|
8
|
-
"risk": "low",
|
|
9
|
-
"input_schema": {
|
|
10
|
-
"type": "object",
|
|
11
|
-
"properties": {
|
|
12
|
-
"location": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
"description": "The location to get weather for (city name, address, etc.)"
|
|
15
|
-
},
|
|
16
|
-
"units": {
|
|
17
|
-
"type": "string",
|
|
18
|
-
"enum": ["celsius", "fahrenheit"],
|
|
19
|
-
"description": "Temperature units to use (default: fahrenheit)"
|
|
20
|
-
},
|
|
21
|
-
"days": {
|
|
22
|
-
"type": "number",
|
|
23
|
-
"description": "Number of forecast days to return (1-16, default: 10)"
|
|
24
|
-
},
|
|
25
|
-
"reason": {
|
|
26
|
-
"type": "string",
|
|
27
|
-
"description": "Brief non-technical explanation of why this tool is being called"
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
"required": ["location"]
|
|
31
|
-
},
|
|
32
|
-
"executor": "tools/get-weather.ts",
|
|
33
|
-
"execution_target": "host"
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<rect x="0" y="0" width="16" height="16" fill="#87CEEB"/>
|
|
3
|
-
<rect x="3" y="2" width="10" height="1" fill="#FFFFFF"/>
|
|
4
|
-
<rect x="2" y="3" width="12" height="1" fill="#FFFFFF"/>
|
|
5
|
-
<rect x="1" y="4" width="14" height="2" fill="#FFFFFF"/>
|
|
6
|
-
<rect x="2" y="6" width="12" height="1" fill="#FFFFFF"/>
|
|
7
|
-
<rect x="3" y="7" width="10" height="1" fill="#FFFFFF"/>
|
|
8
|
-
<rect x="4" y="8" width="8" height="1" fill="#87CEEB"/>
|
|
9
|
-
<rect x="2" y="9" width="3" height="1" fill="#4A90E2"/>
|
|
10
|
-
<rect x="6" y="9" width="1" height="1" fill="#4A90E2"/>
|
|
11
|
-
<rect x="9" y="9" width="1" height="1" fill="#4A90E2"/>
|
|
12
|
-
<rect x="12" y="9" width="2" height="1" fill="#4A90E2"/>
|
|
13
|
-
<rect x="1" y="10" width="4" height="1" fill="#4A90E2"/>
|
|
14
|
-
<rect x="6" y="10" width="1" height="1" fill="#4A90E2"/>
|
|
15
|
-
<rect x="9" y="10" width="1" height="1" fill="#4A90E2"/>
|
|
16
|
-
<rect x="12" y="10" width="3" height="1" fill="#4A90E2"/>
|
|
17
|
-
<rect x="2" y="11" width="3" height="1" fill="#4A90E2"/>
|
|
18
|
-
<rect x="6" y="11" width="1" height="1" fill="#4A90E2"/>
|
|
19
|
-
<rect x="9" y="11" width="1" height="1" fill="#4A90E2"/>
|
|
20
|
-
<rect x="12" y="11" width="2" height="1" fill="#4A90E2"/>
|
|
21
|
-
<rect x="3" y="12" width="2" height="1" fill="#4A90E2"/>
|
|
22
|
-
<rect x="7" y="12" width="1" height="1" fill="#4A90E2"/>
|
|
23
|
-
<rect x="11" y="12" width="2" height="1" fill="#4A90E2"/>
|
|
24
|
-
</svg>
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ToolContext,
|
|
3
|
-
ToolExecutionResult,
|
|
4
|
-
} from "../../../../tools/types.js";
|
|
5
|
-
import { executeGetWeather } from "../../../../tools/weather/service.js";
|
|
6
|
-
|
|
7
|
-
export async function run(
|
|
8
|
-
input: Record<string, unknown>,
|
|
9
|
-
context: ToolContext,
|
|
10
|
-
): Promise<ToolExecutionResult> {
|
|
11
|
-
return executeGetWeather(input, globalThis.fetch, context.proxyToolResolver);
|
|
12
|
-
}
|