feat(ai): add atm10 expert agent

This commit is contained in:
Guillaume ARM 2026-06-11 18:34:32 +02:00
parent 04983f97c5
commit 5a950de0ba
16 changed files with 432 additions and 57 deletions

View File

@ -0,0 +1,30 @@
# ATM10 Expert Context Index
Local context for the `atm10-expert` opencode agent.
## Target Pack
- ATM10 / All The Mods 10 version `7.0` (`0.7.0` is accepted as a user alias).
- Minecraft `1.21.1`.
- NeoForge `21.1.228`.
- CurseForge project id `925200`.
- Client pack file id `8091114`.
- Server pack file id `8094893`.
- Source: [AllTheMods/ATM-10](https://github.com/AllTheMods/ATM-10).
## Local Glossaries
- [CC:Tweaked glossary](glossaries/cc_glossary.md) - globals, modules, peripherals, events, and guides.
- [Advanced Peripherals glossary](glossaries/advanced_peripherals_glossary.md) - Advanced Peripherals 0.7 guides, peripherals, turtles, integrations, and changelog pages.
- [Create CC:Tweaked glossary](glossaries/create_cc_tweaked_glossary.md) - Create CC:Tweaked integration pages.
## Modpack Notes
- [ATM10 7.0 modpack](modpacks/atm10-7.0.md) - pack metadata and generated CurseForge mod ids.
## Update Workflow
- Use local glossaries first when answering in-game questions.
- Use web lookup when local context is missing, stale, or too shallow.
- When a useful durable documentation source is found, update or create a glossary and update this index.
- Refresh the mod id list with `./.opencode/agent-context/atm10-expert/scripts/fetch-atm10-7.0-mods.sh`.

View File

@ -0,0 +1,20 @@
# ATM10 7.0 Modpack
Known facts for ATM10 / All The Mods 10 version `7.0`.
## Pack Metadata
- Minecraft: `1.21.1`.
- NeoForge: `21.1.228`.
- CurseForge project id: `925200`.
- Client pack file id: `8091114`.
- Server pack file id: `8094893`.
- Source: [AllTheMods/ATM-10](https://github.com/AllTheMods/ATM-10).
## Mod List
The mod list is generated by [`../scripts/fetch-atm10-7.0-mods.sh`](../scripts/fetch-atm10-7.0-mods.sh).
Run it with `CURSEFORGE_API_KEY` to resolve project names through the CurseForge API. Without an API key, the generated table preserves raw project/file ids and API URLs for later enrichment.
Generated list: not fetched yet.

View File

@ -0,0 +1,93 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECT_ID=925200
FILE_ID=8091114
SERVER_FILE_ID=8094893
NEOFORGE_VERSION=21.1.228
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
CONTEXT_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
OUTPUT="${CONTEXT_DIR}/modpacks/atm10-7.0.md"
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "${TMP_DIR}"
}
trap cleanup EXIT
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
printf 'missing required command: %s\n' "$1" >&2
exit 1
fi
}
require_cmd curl
require_cmd jq
require_cmd unzip
ARCHIVE="${TMP_DIR}/atm10-${FILE_ID}.zip"
MANIFEST="${TMP_DIR}/manifest.json"
PROJECTS="${TMP_DIR}/projects.json"
download_url=''
if [ -n "${CURSEFORGE_API_KEY:-}" ]; then
download_url="$(curl -fsS \
-H "x-api-key: ${CURSEFORGE_API_KEY}" \
"https://api.curseforge.com/v1/mods/${PROJECT_ID}/files/${FILE_ID}/download-url" \
| jq -r '.data // empty')"
fi
if [ -n "${download_url}" ]; then
curl -fL --retry 3 -o "${ARCHIVE}" "${download_url}"
else
curl -fL --retry 3 -o "${ARCHIVE}" \
"https://www.curseforge.com/api/v1/mods/${PROJECT_ID}/files/${FILE_ID}/download"
fi
unzip -p "${ARCHIVE}" manifest.json > "${MANIFEST}"
if [ -n "${CURSEFORGE_API_KEY:-}" ]; then
mod_ids="$(jq -c '[.files[].projectID] | unique' "${MANIFEST}")"
curl -fsS \
-H "x-api-key: ${CURSEFORGE_API_KEY}" \
-H 'content-type: application/json' \
-d "{\"modIds\":${mod_ids}}" \
'https://api.curseforge.com/v1/mods' > "${PROJECTS}"
else
printf '{"data":[]}\n' > "${PROJECTS}"
fi
{
printf '# ATM10 7.0 Modpack\n\n'
printf 'Generated from CurseForge client file `%s`.\n\n' "${FILE_ID}"
printf 'Last generated: `%s`.\n\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
printf '## Pack Metadata\n\n'
printf -- '- Minecraft: `%s`.\n' "$(jq -r '.minecraft.version // "1.21.1"' "${MANIFEST}")"
printf -- '- NeoForge: `%s`.\n' "${NEOFORGE_VERSION}"
printf -- '- CurseForge project id: `%s`.\n' "${PROJECT_ID}"
printf -- '- Client pack file id: `%s`.\n' "${FILE_ID}"
printf -- '- Server pack file id: `%s`.\n' "${SERVER_FILE_ID}"
printf -- '- Source: [AllTheMods/ATM-10](https://github.com/AllTheMods/ATM-10).\n\n'
printf '## Manifest\n\n'
jq -r '"- Name: `" + (.name // "unknown") + "`.", "- Version: `" + (.version // "unknown") + "`."' "${MANIFEST}"
printf '\n## Mods\n\n'
printf '| Project id | File id | Name | Reference |\n'
printf '| --- | --- | --- | --- |\n'
jq -r --slurpfile projects "${PROJECTS}" '
def project($id): (($projects[0].data // [])[]? | select(.id == $id)) // {};
def cell: tostring | gsub("\\|"; "\\\\|");
.files[]
| project(.projectID) as $project
| [
(.projectID | tostring),
(.fileID | tostring),
(($project.name // "unresolved") | cell),
(($project.links.websiteUrl // ("https://api.curseforge.com/v1/mods/" + (.projectID | tostring))) | cell)
]
| "| " + join(" | ") + " |"
' "${MANIFEST}"
} > "${OUTPUT}"
printf 'wrote %s\n' "${OUTPUT}"

View File

@ -0,0 +1,61 @@
---
description: Answers in-game ATM10, ComputerCraft, CC:Tweaked, Advanced Peripherals, Create, and TrapOS user questions with concise player-facing replies; use for programs/ai.lua requests when opencc.agent is atm10-expert.
mode: primary
permission:
"*": deny
read: allow
glob: allow
grep: allow
webfetch: allow
websearch: allow
edit: ask
bash:
"*": ask
"just check": allow
computercraft-mcp-bridge_probe-computers: allow
computercraft-mcp-bridge_exec-lua: allow
---
You answer players using TrapOS `ai` from inside ATM10 / All The Mods 10.
Target environment:
- ATM10 / All The Mods 10 pack version `7.0`; accept `0.7.0` as the same user alias.
- Minecraft `1.21.1`.
- NeoForge `21.1.228`.
- ComputerCraft is CC:Tweaked in the ComputerCraft sandbox, not desktop Lua.
Response style:
- Keep replies short for in-game terminals. Prefer 1-4 concise lines.
- Default to practical, non-technical answers unless the user asks for details.
- Answer in the user's language; usually French when the prompt is French.
- When giving commands, make them directly runnable in CraftOS when possible.
- When giving code, keep it minimal and runnable. Avoid markdown fences unless the user clearly asks for a code block.
- If the prompt asks for raw Lua code only, output raw Lua only with no markdown or explanation.
Context workflow:
- Use `.opencode/agent-context/atm10-expert/INDEX.md` first.
- Prefer local glossaries and modpack notes before web lookup.
- Use web search or fetch only for current external facts, missing mod documentation, or stale local references.
- If you find a useful durable mod documentation source, ask before editing, then update or create a glossary and update `INDEX.md`.
MCP bridge safety:
- You may use the ComputerCraft MCP bridge only through `probe-computers` and `exec-lua`.
- Use `probe-computers` before `exec-lua` unless the target computer id is already clear from the hidden caller context or conversation.
- Treat `exec-lua` as privileged in-game execution. Prefer read-only inspection.
- Do not delete files, reboot or shut down computers, move turtles, change inventories, transmit network traffic, mutate peripherals, or run long loops unless the user explicitly asks for that specific effect.
- Keep `exec-lua` snippets small and bounded. Use short timeouts. Avoid blocking pulls, sleeps, infinite loops, and assumptions that a timeout stops code already running in ComputerCraft.
- `print()` and `write()` output is captured in MCP results. To intentionally write to the visible ComputerCraft screen, use terminal APIs such as `term.clear()`, `term.setCursorPos()`, and `term.write()`.
Caller context:
- TrapOS may prepend hidden caller context with the ComputerCraft computer id and label.
- Use that context to choose the right MCP target, but do not expose it unless it helps the user.
Repository scope:
- If the user asks for repo internals, answer only what is needed to unblock them.
- This agent is explicit and player-facing. Do not assume it is the default coding assistant.

View File

@ -1,37 +0,0 @@
---
description: Answers in-game ComputerCraft and TrapOS user questions with concise, actionable replies; use for programs/ai.lua requests.
mode: primary
permission:
"*": deny
websearch: allow
computercraft-mcp-bridge_probe-computers: allow
computercraft-mcp-bridge_exec-lua: allow
---
You answer ComputerCraft / CC:Tweaked users from inside Minecraft through TrapOS `ai`.
Keep replies extremely concise. Prefer 1-4 short lines. Avoid long explanations, tables, and broad background.
Assume the user is reading on an in-game ComputerCraft terminal with limited space.
Use ComputerCraft Lua and CC:Tweaked behavior, not standard desktop Lua, unless explicitly asked otherwise.
When giving commands, make them directly runnable in CraftOS when possible.
When giving code, keep it minimal and runnable. Avoid markdown fences unless the user clearly asks for a code block.
You may use web search for current external facts and documentation. Keep searches focused and summarize only what unblocks the user.
You may use the ComputerCraft MCP bridge only through `probe-computers` and `exec-lua`.
Use `probe-computers` before `exec-lua` unless the target computer id is already clear from the conversation.
Treat `exec-lua` as privileged in-game execution. Prefer read-only inspection. Do not delete files, reboot or shut down computers, move turtles, change inventories, transmit network traffic, mutate peripherals, or run long loops unless the user explicitly asks for that specific effect.
Keep `exec-lua` snippets small and bounded. Use short timeouts. Avoid blocking pulls, sleeps, infinite loops, and assumptions that a timeout stops code already running in ComputerCraft.
`print()` and `write()` output is captured in MCP results. To intentionally write to the visible ComputerCraft screen, use terminal APIs such as `term.clear()`, `term.setCursorPos()`, and `term.write()`.
If the user asks for repo internals, answer only what is needed to unblock them.
If the prompt asks for raw Lua code only, output raw Lua only with no markdown or explanation.

109
ATM_EXPERT_AGENT_PLAN.md Normal file
View File

@ -0,0 +1,109 @@
# ATM Expert Agent Plan
## Goal
Replace `.opencode/agent/computercraft.md` with `atm10-expert`.
The agent targets ATM10 / All The Mods 10, Minecraft `1.21.1`, NeoForge, pack version `7.0` (`0.7.0` accepted as user alias).
## Known Facts
CurseForge project id: `925200`.
ATM10 `7.0` file id: `8091114`.
Server pack file id: `8094893`.
NeoForge version: `21.1.228`.
GitHub source: <https://github.com/AllTheMods/ATM-10>
## Files To Create
Create `.opencode/agent/atm10-expert.md`.
Create `.opencode/agent-context/atm10-expert/INDEX.md`.
Create `.opencode/agent-context/atm10-expert/glossaries/`.
Create `.opencode/agent-context/atm10-expert/scripts/`.
Create `.opencode/agent-context/atm10-expert/modpacks/atm10-7.0.md`.
## Files To Move
Move in-game glossaries only:
`docs/cc_glossary.md` to agent context.
`docs/create_cc_tweaked_glossary.md` to agent context.
`docs/advanced_peripherals_glossary.md` to agent context.
Keep `docs/craftos_pc_glossary.md` in `docs/`.
Update `docs/README.md` links.
## Agent Behavior
Delete old `computercraft` agent.
New agent answers players concisely for in-game terminals.
Use local glossaries first, then websearch/webfetch.
Default to non-technical answers.
Adapt language to the user, usually French.
If a new useful mod documentation source is found, update or create a glossary and update `INDEX.md`.
Keep replies short unless the user asks for detail.
Use MCP bridge carefully: probe first, execute only bounded/read-only Lua unless explicitly asked.
## Permissions
Allow local read/search and web lookup.
Keep MCP bridge `probe-computers` and `exec-lua`.
Make edit/bash approval-gated or tightly limited, because this agent is player-facing.
Allow `just check` for markdown validation.
## ATM10 Mod List Script
Add `.opencode/agent-context/atm10-expert/scripts/fetch-atm10-7.0-mods.sh`.
Script should download CurseForge file `8091114`.
Extract `manifest.json`.
Write raw project/file ids and best available names to `modpacks/atm10-7.0.md`.
If `CURSEFORGE_API_KEY` exists, resolve project names through the CurseForge API.
Without API key, preserve ids and URLs for later glossary enrichment.
## TrapOS AI Context
Update `programs/ai.lua` / `apis/libai.lua` so each prompt includes hidden caller context:
computer id.
computer label, if any.
This helps `atm10-expert` know which in-game computer is calling.
Add tests in `tests/ai.lua`.
Bump `packages/trapos-ai/ccpm.json` and `packages/index.json`.
## Verification
Run `just check`.
Fix Lua lint and markdown link issues.
Restart opencode after agent/config changes.

View File

@ -165,6 +165,35 @@ local function buildLuaOutputPrompt(userPrompt, output)
}, '\n'); }, '\n');
end end
local function readOsValue(osLib, name)
if type(osLib) ~= 'table' or type(osLib[name]) ~= 'function' then
return nil;
end
local ok, value = pcall(osLib[name]);
if not ok then return nil; end
return value;
end
local function buildPromptWithCallerContext(prompt, osLib)
local lines = {
'<caller-context hidden="true">',
'Use this context silently to identify the in-game ComputerCraft caller.',
};
local computerId = readOsValue(osLib, 'getComputerID');
local computerLabel = readOsValue(osLib, 'getComputerLabel');
if computerId ~= nil then
lines[#lines + 1] = 'computer id: ' .. tostring(computerId);
end
if not isBlank(computerLabel) then
lines[#lines + 1] = 'computer label: ' .. tostring(computerLabel);
end
lines[#lines + 1] = '</caller-context>';
lines[#lines + 1] = '';
lines[#lines + 1] = 'User prompt:';
lines[#lines + 1] = prompt;
return table.concat(lines, '\n');
end
local function sessionTime(session) local function sessionTime(session)
if type(session) ~= 'table' or type(session.time) ~= 'table' then if type(session) ~= 'table' or type(session.time) ~= 'table' then
return 0; return 0;
@ -179,6 +208,7 @@ local function createAi(opts)
local settingsLib = opts.settings or settings; local settingsLib = opts.settings or settings;
local eventloopFactory = opts.eventloop or require('/apis/eventloop'); local eventloopFactory = opts.eventloop or require('/apis/eventloop');
local nowFunc = opts.now or nowSeconds; local nowFunc = opts.now or nowSeconds;
local osLib = opts.os or os;
local api = {}; local api = {};
@ -566,15 +596,20 @@ local function createAi(opts)
log('reusing session ' .. sessionId); log('reusing session ' .. sessionId);
end end
local promptWithContext = prompt;
if options.includeCallerContext ~= false then
promptWithContext = buildPromptWithCallerContext(prompt, osLib);
end
if not (cfg.providerID and cfg.modelID) then if not (cfg.providerID and cfg.modelID) then
log('provider/model unset; using blocking message endpoint'); log('provider/model unset; using blocking message endpoint');
return askBlocking(cfg, sessionId, prompt, persist, sessionSettingKey, log); return askBlocking(cfg, sessionId, promptWithContext, persist, sessionSettingKey, log);
end end
local messageId = options.messageId or createMessageId(); local messageId = options.messageId or createMessageId();
log('sending async prompt ' .. messageId); log('sending async prompt ' .. messageId);
local body, code = doPost(cfg, '/session/' .. sessionId .. '/prompt_async', local body, code = doPost(cfg, '/session/' .. sessionId .. '/prompt_async',
buildPromptBody(cfg, messageId, prompt)); buildPromptBody(cfg, messageId, promptWithContext));
if not body then return false, code; end if not body then return false, code; end
if code == 404 then if code == 404 then
return handleMissingSession(persist, sessionSettingKey); return handleMissingSession(persist, sessionSettingKey);
@ -760,6 +795,8 @@ local function createAi(opts)
end end
function api.ping(options) function api.ping(options)
options = options or {};
options.includeCallerContext = false;
return api.ask(PING_PROMPT, options); return api.ask(PING_PROMPT, options);
end end

View File

@ -4,10 +4,8 @@ Start here when looking up ComputerCraft-related APIs, CraftOS-PC behavior, peri
## Indexes ## Indexes
- [`cc_glossary.md`](cc_glossary.md) - CC:Tweaked globals, modules, peripherals, events, and guides.
- [`craftos_pc_glossary.md`](craftos_pc_glossary.md) - CraftOS-PC emulator setup, CLI flags, mounting, peripheral emulation, troubleshooting, and related references. - [`craftos_pc_glossary.md`](craftos_pc_glossary.md) - CraftOS-PC emulator setup, CLI flags, mounting, peripheral emulation, troubleshooting, and related references.
- [`advanced_peripherals_glossary.md`](advanced_peripherals_glossary.md) - Advanced Peripherals 0.7 guides, peripherals, turtles, integrations, and changelog pages. - [`../.opencode/agent-context/atm10-expert/INDEX.md`](../.opencode/agent-context/atm10-expert/INDEX.md) - ATM10 in-game agent context, including CC:Tweaked, Advanced Peripherals, and Create CC:Tweaked glossaries.
- [`create_cc_tweaked_glossary.md`](create_cc_tweaked_glossary.md) - Create CC:Tweaked integration pages.
- [`opencode_server_guide.md`](opencode_server_guide.md) - Running `opencode serve` for the TrapOS `ai` client. - [`opencode_server_guide.md`](opencode_server_guide.md) - Running `opencode serve` for the TrapOS `ai` client.
- [`ingame-trapos-ai-mcp-guide.md`](ingame-trapos-ai-mcp-guide.md) - Concise in-game checklist for installing TrapOS, connecting `ai`, and linking MCP. - [`ingame-trapos-ai-mcp-guide.md`](ingame-trapos-ai-mcp-guide.md) - Concise in-game checklist for installing TrapOS, connecting `ai`, and linking MCP.
- [`opencode_api.md`](opencode_api.md) - Minimal opencode HTTP API reference used by TrapOS. - [`opencode_api.md`](opencode_api.md) - Minimal opencode HTTP API reference used by TrapOS.

View File

@ -52,7 +52,7 @@ set opencc.model_id claude-opus-4-7
Optional agent setting for the in-game ComputerCraft assistant: Optional agent setting for the in-game ComputerCraft assistant:
```sh ```sh
set opencc.agent computercraft set opencc.agent atm10-expert
``` ```
Test it: Test it:

View File

@ -88,7 +88,7 @@ Send a message and wait for the AI reply (blocking). Returns when the assistant
"parts": [ "parts": [
{ "type": "text", "text": "your prompt here" } { "type": "text", "text": "your prompt here" }
], ],
"agent": "computercraft", "agent": "atm10-expert",
"model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" } "model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" }
} }
``` ```
@ -153,7 +153,7 @@ Fire-and-forget variant. Returns `204` immediately. Include `messageID` in the r
"parts": [ "parts": [
{ "type": "text", "text": "your prompt here" } { "type": "text", "text": "your prompt here" }
], ],
"agent": "computercraft", "agent": "atm10-expert",
"model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" } "model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" }
} }
``` ```

View File

@ -107,7 +107,7 @@ set opencc.username myuser
Optional — select an opencode agent for requests from this computer: Optional — select an opencode agent for requests from this computer:
```sh ```sh
set opencc.agent computercraft set opencc.agent atm10-expert
``` ```
Optional — scope `ai sessions` to a specific opencode project directory. If omitted, `ai sessions` falls back to the directory recorded on the saved `opencc.session_id` when the unscoped list is empty: Optional — scope `ai sessions` to a specific opencode project directory. If omitted, `ai sessions` falls back to the directory recorded on the saved `opencc.session_id` when the unscoped list is empty:
@ -144,7 +144,7 @@ ai ping -- ping, reuses existing session
ai "explain what a turtle is" ai "explain what a turtle is"
ai new "start a fresh topic" -- forget current session, start fresh ai new "start a fresh topic" -- forget current session, start fresh
ai sessions -- list all server sessions with their IDs ai sessions -- list all server sessions with their IDs
ai --agent computercraft "what peripherals are attached?" ai --agent atm10-expert "what peripherals are attached?"
ai --verbose ping -- show HTTP/session diagnostics ai --verbose ping -- show HTTP/session diagnostics
ai --help ai --help
ai --version ai --version

View File

@ -49,7 +49,7 @@ local function printUsage()
print(' opencc.password (Basic Auth password)'); print(' opencc.password (Basic Auth password)');
print(' opencc.session_id (auto-managed)'); print(' opencc.session_id (auto-managed)');
print(' opencc.directory (optional session list scope)'); print(' opencc.directory (optional session list scope)');
print(' opencc.agent (e.g. computercraft)'); print(' opencc.agent (e.g. atm10-expert)');
print(' opencc.provider_id (e.g. anthropic)'); print(' opencc.provider_id (e.g. anthropic)');
print(' opencc.model_id (e.g. claude-opus-4-7)'); print(' opencc.model_id (e.g. claude-opus-4-7)');
print(' opencc.timeout_seconds (per HTTP call, max 60)'); print(' opencc.timeout_seconds (per HTTP call, max 60)');

View File

@ -21,6 +21,13 @@ local function fakeSettings(initial)
}; };
end end
local function fakeOs(computerId, computerLabel)
return {
getComputerID = function() return computerId; end,
getComputerLabel = function() return computerLabel; end,
};
end
local function fakeAsyncSettings(initial) local function fakeAsyncSettings(initial)
local values = { local values = {
['opencc.server_url'] = 'http://host', ['opencc.server_url'] = 'http://host',
@ -429,7 +436,7 @@ testlib.test('ask falls back to blocking message when model is unset', function(
testlib.assertEquals(#httpStub.getCalls, 0); testlib.assertEquals(#httpStub.getCalls, 0);
end); end);
testlib.test('ask sends exact prompt text', function() testlib.test('ask wraps prompt with caller context', function()
local httpStub = fakeHttp( local httpStub = fakeHttp(
{ messageResp('reply') }, { messageResp('reply') },
{} {}
@ -438,14 +445,43 @@ testlib.test('ask sends exact prompt text', function()
['opencc.server_url'] = 'http://host', ['opencc.server_url'] = 'http://host',
['opencc.session_id'] = 'ses_1', ['opencc.session_id'] = 'ses_1',
}); });
local ai = createAi({ http = httpStub, settings = settingsStub }); local ai = createAi({
http = httpStub,
settings = settingsStub,
os = fakeOs(42, 'storage-main'),
});
ai.ask('my prompt'); ai.ask('my prompt');
local body = textutils.unserializeJSON(httpStub.postCalls[1].body); local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(#body.parts, 1); testlib.assertEquals(#body.parts, 1);
testlib.assertEquals(body.parts[1].type, 'text'); testlib.assertEquals(body.parts[1].type, 'text');
testlib.assertEquals(body.parts[1].text, 'my prompt'); testlib.assertTrue(string.find(body.parts[1].text, '<caller-context hidden="true">', 1, true) ~= nil);
testlib.assertTrue(string.find(body.parts[1].text, 'computer id: 42', 1, true) ~= nil);
testlib.assertTrue(string.find(body.parts[1].text, 'computer label: storage-main', 1, true) ~= nil);
testlib.assertTrue(string.find(body.parts[1].text, 'User prompt:\nmy prompt', 1, true) ~= nil);
end);
testlib.test('ask omits blank caller label', function()
local httpStub = fakeHttp(
{ messageResp('reply') },
{}
);
local settingsStub = fakeSettings({
['opencc.server_url'] = 'http://host',
['opencc.session_id'] = 'ses_1',
});
local ai = createAi({
http = httpStub,
settings = settingsStub,
os = fakeOs(7, ''),
});
ai.ask('my prompt');
local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertTrue(string.find(body.parts[1].text, 'computer id: 7', 1, true) ~= nil);
testlib.assertEquals(string.find(body.parts[1].text, 'computer label:', 1, true), nil);
end); end);
testlib.test('ask includes agent from settings', function() testlib.test('ask includes agent from settings', function()
@ -456,14 +492,14 @@ testlib.test('ask includes agent from settings', function()
local settingsStub = fakeSettings({ local settingsStub = fakeSettings({
['opencc.server_url'] = 'http://host', ['opencc.server_url'] = 'http://host',
['opencc.session_id'] = 'ses_1', ['opencc.session_id'] = 'ses_1',
['opencc.agent'] = 'computercraft', ['opencc.agent'] = 'atm10-expert',
}); });
local ai = createAi({ http = httpStub, settings = settingsStub }); local ai = createAi({ http = httpStub, settings = settingsStub });
ai.ask('hello'); ai.ask('hello');
local body = textutils.unserializeJSON(httpStub.postCalls[1].body); local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(body.agent, 'computercraft'); testlib.assertEquals(body.agent, 'atm10-expert');
end); end);
testlib.test('ask option agent overrides settings', function() testlib.test('ask option agent overrides settings', function()
@ -478,10 +514,10 @@ testlib.test('ask option agent overrides settings', function()
}); });
local ai = createAi({ http = httpStub, settings = settingsStub }); local ai = createAi({ http = httpStub, settings = settingsStub });
ai.ask('hello', { agent = 'computercraft' }); ai.ask('hello', { agent = 'atm10-expert' });
local body = textutils.unserializeJSON(httpStub.postCalls[1].body); local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(body.agent, 'computercraft'); testlib.assertEquals(body.agent, 'atm10-expert');
end); end);
testlib.test('ask omits blank agent setting', function() testlib.test('ask omits blank agent setting', function()
@ -624,7 +660,7 @@ testlib.test('ask includes agent in async prompts', function()
); );
local settingsStub = fakeAsyncSettings({ local settingsStub = fakeAsyncSettings({
['opencc.session_id'] = 'ses_1', ['opencc.session_id'] = 'ses_1',
['opencc.agent'] = 'computercraft', ['opencc.agent'] = 'atm10-expert',
}); });
local elFactory = fakeEventloopFactory(); local elFactory = fakeEventloopFactory();
local ai = createAi({ local ai = createAi({
@ -638,7 +674,35 @@ testlib.test('ask includes agent in async prompts', function()
testlib.assertTrue(ok); testlib.assertTrue(ok);
local body = textutils.unserializeJSON(httpStub.postCalls[1].body); local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertEquals(body.agent, 'computercraft'); testlib.assertEquals(body.agent, 'atm10-expert');
end);
testlib.test('ask includes caller context in async prompts', function()
local httpStub = fakeHttp(
{ asyncResp() },
{
messageListResp({ assistantMessage('msg_1', 'reply', true) }),
}
);
local settingsStub = fakeAsyncSettings({
['opencc.session_id'] = 'ses_1',
});
local elFactory = fakeEventloopFactory();
local ai = createAi({
http = httpStub,
settings = settingsStub,
now = function() return 10; end,
eventloop = elFactory,
os = fakeOs(99, 'factory'),
});
local ok = ai.ask('status?', { messageId = 'msg_1' });
testlib.assertTrue(ok);
local body = textutils.unserializeJSON(httpStub.postCalls[1].body);
testlib.assertTrue(string.find(body.parts[1].text, 'computer id: 99', 1, true) ~= nil);
testlib.assertTrue(string.find(body.parts[1].text, 'computer label: factory', 1, true) ~= nil);
testlib.assertTrue(string.find(body.parts[1].text, 'User prompt:\nstatus?', 1, true) ~= nil);
end); end);
testlib.test('ask polls async message until completion', function() testlib.test('ask polls async message until completion', function()