Adds the one thing the extension could not answer before: roughly where the user actually is, rather than which zone their clock is set to. - `lib/geo.ts` resolves a place from ipapi.co. Chosen because it is HTTPS and needs no API key; ip-api.com, the other obvious free option, is HTTP-only on its free tier, which is not a place to send an IP address. - Off by default, and skipped entirely when the user has set Location: the better answer is already there, so spending their privacy on a worse one would be indefensible. `ipPlace` is only wired into ToolContext when the preference is on, so an off setting cannot reach the network at all. - Cached for 6 hours. A city does not change between questions, and every lookup hands the provider another copy of the address. Set Up Lam Agent clears it on re-test, alongside the vision cache. - Failures are returned, not swallowed: a rate limit or an offline machine says so in the tool output while the region answer stays available. The provider signals some failures with error: true and HTTP 200, so the body is checked as well as the status. - The tool output always names the provider and calls the result city-level rather than exact. It also flags an IP timezone that disagrees with the device clock, which usually means a VPN. - The README claimed no geolocation service ever saw the user's address. That is now conditional, so the section was rewritten rather than left to quietly become untrue. Deliberately not done: GPS. CoreLocation has no CLI on macOS and GeoClue's usual provider resolves nearby Wi-Fi through a Mozilla service anyway. Verified: 341 tests (25 new), tsc, eslint, prettier, and vici build. I did not make a live API call, because doing so would send this machine's IP to the provider as the exact flow being opted into.
169 lines
6.3 KiB
JSON
169 lines
6.3 KiB
JSON
{
|
|
"$schema": "https://raw.githubusercontent.com/vicinaehq/vicinae/refs/heads/main/extra/schemas/extension.json",
|
|
"name": "lam-agent",
|
|
"title": "Lam Agent",
|
|
"description": "Agentic AI chat powered by Ollama. Ask anything from a global hotkey, with streaming answers, persistent sessions, and web search.",
|
|
"icon": "extension-icon.png",
|
|
"author": "adrianbonpin",
|
|
"categories": ["Productivity", "Developer Tools"],
|
|
"license": "MIT",
|
|
"commands": [
|
|
{
|
|
"name": "chat",
|
|
"title": "Chat with Lam",
|
|
"subtitle": "Lam Agent",
|
|
"description": "Ask Lam anything. Answers stream in as they are generated, your session history is kept, and Lam can search the web when it needs current information.",
|
|
"mode": "view"
|
|
},
|
|
{
|
|
"name": "snip",
|
|
"title": "Snip to Lam",
|
|
"subtitle": "Lam Agent",
|
|
"description": "Capture a region of the screen and start a new chat about it. The launcher window closes first so it cannot end up in the shot. Offered only when the model in preferences supports image input.",
|
|
"mode": "no-view"
|
|
},
|
|
{
|
|
"name": "sessions",
|
|
"title": "Chat Sessions",
|
|
"description": "Browse, reopen, and clean up your past conversations with Lam.",
|
|
"mode": "view"
|
|
},
|
|
{
|
|
"name": "setup",
|
|
"title": "Set Up Lam Agent",
|
|
"description": "Check your Ollama connection, pick the model Lam should use, and tune how Lam answers.",
|
|
"mode": "view"
|
|
}
|
|
],
|
|
"preferences": [
|
|
{
|
|
"name": "apiKey",
|
|
"title": "Ollama API Key",
|
|
"description": "Your Ollama API key, created at ollama.com/settings/keys. Required to reach cloud models and the web search tools.",
|
|
"type": "password",
|
|
"required": true
|
|
},
|
|
{
|
|
"name": "model",
|
|
"title": "Model",
|
|
"description": "The model Lam should use, for example deepseek-v4.1-flash:cloud or gpt-oss:120b. Run \"Set Up Lam Agent\" to pick from the models your key can access.",
|
|
"type": "textfield",
|
|
"required": true
|
|
},
|
|
{
|
|
"name": "ollamaHost",
|
|
"title": "Ollama Host",
|
|
"description": "Base URL of the Ollama API. Leave this alone for Ollama Cloud, or point it at http://localhost:11434 to use a local Ollama server.",
|
|
"type": "textfield",
|
|
"required": false,
|
|
"default": "https://ollama.com"
|
|
},
|
|
{
|
|
"name": "systemPrompt",
|
|
"title": "System Prompt",
|
|
"description": "Extra instructions prepended to every conversation. Leave empty to use Lam's default prompt.",
|
|
"type": "textfield",
|
|
"required": false
|
|
},
|
|
{
|
|
"name": "location",
|
|
"title": "Location",
|
|
"description": "The place Lam should treat as yours, for example Cebu City, Philippines. Optional. Without it Lam falls back to your device timezone and locale, which are accurate to a region and not to a city, so anything that depends on exactly where you are (weather, opening hours) is worth filling in.",
|
|
"type": "textfield",
|
|
"required": false
|
|
},
|
|
{
|
|
"name": "ipGeolocation",
|
|
"title": "IP Geolocation",
|
|
"label": "Look up my city from my IP address",
|
|
"description": "Off by default. When this is on, and the Location preference above is empty, Lam sends your IP address to ipapi.co to work out your city. That is a third party learning roughly where you are, which is why it is opt-in, and why setting Location makes it unnecessary.",
|
|
"type": "checkbox",
|
|
"required": false,
|
|
"default": false
|
|
},
|
|
{
|
|
"name": "webSearchEnabled",
|
|
"title": "Web Tools",
|
|
"label": "Let Lam search the web",
|
|
"description": "Let Lam search the web and fetch pages when a question needs up-to-date information.",
|
|
"type": "checkbox",
|
|
"required": false,
|
|
"default": true
|
|
},
|
|
{
|
|
"name": "thinking",
|
|
"title": "Reasoning Effort",
|
|
"description": "Ask supported models to reason before answering. Higher effort is slower but more accurate. Ignored by models that do not support it.",
|
|
"type": "dropdown",
|
|
"required": false,
|
|
"default": "off",
|
|
"data": [
|
|
{ "title": "Off", "value": "off" },
|
|
{ "title": "Low", "value": "low" },
|
|
{ "title": "Medium", "value": "medium" },
|
|
{ "title": "High", "value": "high" }
|
|
]
|
|
},
|
|
{
|
|
"name": "showToolCalls",
|
|
"title": "Show Tool Calls",
|
|
"label": "Show which tools Lam used",
|
|
"description": "List the tools Lam called under each answer, for example web_search or get_datetime.",
|
|
"type": "checkbox",
|
|
"required": false,
|
|
"default": true
|
|
},
|
|
{
|
|
"name": "showReasoning",
|
|
"title": "Show Reasoning",
|
|
"label": "Show the model's reasoning",
|
|
"description": "Include the model's thinking trace under each answer. Useful for seeing why a model answered the way it did, but it makes replies much longer.",
|
|
"type": "checkbox",
|
|
"required": false,
|
|
"default": true
|
|
},
|
|
{
|
|
"name": "resumeTimeout",
|
|
"title": "Start New Chat After",
|
|
"description": "When the command opens, resume the last conversation only if it was used recently. Past this much idle time it starts a fresh chat instead. The old conversation is never deleted — it stays in the sidebar.",
|
|
"type": "dropdown",
|
|
"required": false,
|
|
"default": "1h",
|
|
"data": [
|
|
{ "title": "Never — always reopen the last chat", "value": "never" },
|
|
{ "title": "After 15 minutes", "value": "15m" },
|
|
{ "title": "After 1 hour", "value": "1h" },
|
|
{ "title": "After 6 hours", "value": "6h" },
|
|
{ "title": "After 1 day", "value": "1d" }
|
|
]
|
|
}
|
|
],
|
|
"dependencies": {
|
|
"@vicinae/api": "^0.29.0",
|
|
"zod": "^4.4.3"
|
|
},
|
|
"devDependencies": {
|
|
"@eslint/js": "^9.36.0",
|
|
"@types/node": "22.13.10",
|
|
"@types/react": "19.1.17",
|
|
"eslint": "^9.22.0",
|
|
"eslint-config-prettier": "^10.1.8",
|
|
"globals": "^16.4.0",
|
|
"prettier": "^3.5.3",
|
|
"react": "19.1.1",
|
|
"typescript": "^5.8.2",
|
|
"typescript-eslint": "^8.45.0",
|
|
"vitest": "^4.1.5"
|
|
},
|
|
"scripts": {
|
|
"dev": "vici develop",
|
|
"build": "vici build",
|
|
"lint": "vici lint",
|
|
"lint:code": "eslint .",
|
|
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
"typecheck": "tsc --noEmit",
|
|
"test": "vitest run",
|
|
"test:watch": "vitest"
|
|
}
|
|
}
|