feat: add aviation and satellite tools

Add aircraft_in_area, find_aircraft, get_aircraft_track and
get_aircraft_info over the live aircraft sources, with an optional
military feed, and next_satellite_pass and satellites_overhead over the
satellite catalog and the existing pass predictor.

Source failures from the live contract now become tool errors with the
matching code, so a rate limit reaches clients as retry_later with a
wait time. Tool schemas may use pattern.
This commit is contained in:
Sameh Khamis committed 2026-10-01 12:05:50 -07:00
1 parent 9895942633
commit 22c3fe40b0
16 files changed
+1111 -16

No files matched your search

+30 -1
View File
@@ -33,6 +33,30 @@ export class ToolError extends Error {
}
}
// Live sources report failures with these codes (src/sources/live/contract.js).
const SOURCE_ERROR_CODES = {
limited: 'retry_later',
denied: 'unavailable',
unavailable: 'unavailable',
malformed: 'malformed',
unsupported: 'unsupported',
};
/** Translate a source failure into a tool error; other errors pass through. */
export function fromSourceError(error) {
const code =
error?.name === 'LiveSourceError' &&
Object.hasOwn(SOURCE_ERROR_CODES, error.code)
? SOURCE_ERROR_CODES[error.code]
: null;
if (!code) return error;
const retryAfterSeconds =
code === 'retry_later' && Number.isFinite(error.retryAfterMs)
? Math.ceil(error.retryAfterMs / 1000)
: null;
return new ToolError(code, error.message, { retryAfterSeconds });
}
/**
* Validate and freeze a tool definition.
*
@@ -114,7 +138,12 @@ export function composeCatalog({
const problems = validateValue(tool.inputSchema, args);
if (problems.length)
throw new ToolError('invalid_arguments', problems.join('; '));
const result = await tool.run(args, { services, signal });
let result;
try {
result = await tool.run(args, { services, signal });
} catch (error) {
throw fromSourceError(error);
}
if (!result || typeof result.summary !== 'string' || !result.data)
throw new TypeError(`${tool.name} returned no summary or data`);
return result;