feat(coding): add model tools and device packages
This commit is contained in:
@@ -8,6 +8,14 @@ import type {
|
||||
RuntimeContextDetailsV1,
|
||||
TaskStateDetailsV1,
|
||||
} from './coding-conversation-contracts';
|
||||
import type { ModelToolDetailsV1, ModelWebSearchErrorCode } from './model-tools';
|
||||
import {
|
||||
DEVICE_PACKAGE_TOOL_NAMES,
|
||||
type DevicePackageIndexV1,
|
||||
type DevicePackageRecordV1,
|
||||
type DevicePackageToolDetailsV1,
|
||||
type InstallPreviewV1,
|
||||
} from './device-packages';
|
||||
import { DATA_SERVICE_PI_TOOL_NAMES } from './data-service';
|
||||
|
||||
const PRODUCT_TOOL_NAMES = new Set([
|
||||
@@ -17,6 +25,8 @@ const PRODUCT_TOOL_NAMES = new Set([
|
||||
'task_state',
|
||||
'changed_file',
|
||||
'runtime_context',
|
||||
'web_search',
|
||||
...DEVICE_PACKAGE_TOOL_NAMES,
|
||||
...DATA_SERVICE_PI_TOOL_NAMES,
|
||||
]);
|
||||
|
||||
@@ -239,6 +249,7 @@ function capabilityDetails(value: Record<string, unknown>): CapabilityResultV1 |
|
||||
|| value.schema !== 'makelore-capability.v1') return null;
|
||||
if (typeof value.plugin_id !== 'string' || value.plugin_id.length > 48
|
||||
|| !CAPABILITY_PLUGIN_ID_PATTERN.test(value.plugin_id)
|
||||
|| value.plugin_id === 'makelore.web-search'
|
||||
|| !text(value.plugin_version, 64)
|
||||
|| typeof value.capability_id !== 'string' || !CAPABILITY_ID_PATTERN.test(value.capability_id)
|
||||
|| typeof value.operation !== 'string' || !CAPABILITY_OPERATION_PATTERN.test(value.operation)
|
||||
@@ -298,6 +309,186 @@ function capabilityDetails(value: Record<string, unknown>): CapabilityResultV1 |
|
||||
};
|
||||
}
|
||||
|
||||
const MODEL_WEB_SEARCH_ERROR_CONTRACT = Object.freeze({
|
||||
model_web_search_unsupported: { httpStatus: 400, retryable: false },
|
||||
model_context_changed: { httpStatus: 409, retryable: false },
|
||||
model_web_search_rate_limited: { httpStatus: 429, retryable: false },
|
||||
model_web_search_unavailable: { httpStatus: 503, retryable: true },
|
||||
model_web_search_invalid_result: { httpStatus: 502, retryable: false },
|
||||
} satisfies Record<ModelWebSearchErrorCode, { httpStatus: number; retryable: boolean }>);
|
||||
|
||||
function modelToolSource(value: unknown): { title: string; url: string } | null {
|
||||
const source = record(value);
|
||||
const title = text(source?.title, 240);
|
||||
const rawUrl = text(source?.url, 2_048);
|
||||
if (!source || !exactKeys(source, ['title', 'url']) || !title || !rawUrl) return null;
|
||||
try {
|
||||
const url = new URL(rawUrl);
|
||||
if ((url.protocol !== 'http:' && url.protocol !== 'https:') || url.username || url.password) return null;
|
||||
return { title, url: url.toString() };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function modelToolDetails(value: Record<string, unknown>): ModelToolDetailsV1 | null {
|
||||
if (value.schema !== 'makelore-model-tool.v1' || value.tool !== 'web_search') return null;
|
||||
const modelId = text(value.modelId, 256);
|
||||
if (!modelId) return null;
|
||||
if (value.status === 'succeeded') {
|
||||
if (!exactKeys(value, [
|
||||
'schema', 'tool', 'status', 'modelId', 'answer', 'sources', 'sourceMode',
|
||||
])) return null;
|
||||
const answer = text(value.answer, 20_000);
|
||||
if (!answer || !Array.isArray(value.sources) || value.sources.length > 20
|
||||
|| (value.sourceMode !== 'structured' && value.sourceMode !== 'inline-or-structured')) return null;
|
||||
const sources = value.sources.map(modelToolSource);
|
||||
if (sources.some((source) => source === null)
|
||||
|| (value.sourceMode === 'structured' && sources.length === 0)) return null;
|
||||
return {
|
||||
schema: 'makelore-model-tool.v1',
|
||||
tool: 'web_search',
|
||||
status: 'succeeded',
|
||||
modelId,
|
||||
answer,
|
||||
sources: sources as Array<{ title: string; url: string }>,
|
||||
sourceMode: value.sourceMode,
|
||||
};
|
||||
}
|
||||
if (value.status !== 'failed'
|
||||
|| !exactKeys(value, ['schema', 'tool', 'status', 'modelId', 'error'])) return null;
|
||||
const error = record(value.error);
|
||||
const code = error?.code;
|
||||
const contract = typeof code === 'string'
|
||||
? MODEL_WEB_SEARCH_ERROR_CONTRACT[code as ModelWebSearchErrorCode]
|
||||
: undefined;
|
||||
const message = text(error?.message, 2_000);
|
||||
if (!error || !contract || !message
|
||||
|| !exactKeys(error, ['code', 'message', 'httpStatus', 'retryable'])
|
||||
|| error.httpStatus !== contract.httpStatus
|
||||
|| error.retryable !== contract.retryable) return null;
|
||||
return {
|
||||
schema: 'makelore-model-tool.v1',
|
||||
tool: 'web_search',
|
||||
status: 'failed',
|
||||
modelId,
|
||||
error: {
|
||||
code: code as ModelWebSearchErrorCode,
|
||||
message,
|
||||
httpStatus: contract.httpStatus as 400 | 409 | 429 | 502 | 503,
|
||||
retryable: contract.retryable,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function deviceSkillEntries(value: unknown): DevicePackageRecordV1['skillEntries'] | null {
|
||||
if (!Array.isArray(value) || value.length > 100) return null;
|
||||
const entries: Array<{ id: string; entryPath: string }> = [];
|
||||
for (const candidate of value) {
|
||||
const item = record(candidate);
|
||||
const id = text(item?.id, 128);
|
||||
const entryPath = text(item?.entryPath, 1_024);
|
||||
if (!item || !exactKeys(item, ['id', 'entryPath']) || !id || !entryPath) return null;
|
||||
entries.push({ id, entryPath });
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
function devicePackageKind(value: unknown): DevicePackageRecordV1['kind'] | null {
|
||||
return value === 'skill-only' || value === 'pi-extension' || value === 'mixed' ? value : null;
|
||||
}
|
||||
|
||||
function devicePackagePreview(value: unknown): InstallPreviewV1 | null {
|
||||
const preview = record(value);
|
||||
if (!preview || !exactKeys(preview, [
|
||||
'schemaVersion', 'planId', 'expiresAt', 'requestedSource', 'resolvedSource',
|
||||
'packageId', 'displayName', 'resolvedVersion', 'kind', 'skillEntries',
|
||||
'extensionEntries', 'includesExecutableCode', 'ignoredLifecycleScripts', 'warnings',
|
||||
'scope',
|
||||
]) || preview.schemaVersion !== 1 || preview.scope !== 'device-parent-workers'
|
||||
|| typeof preview.includesExecutableCode !== 'boolean') return null;
|
||||
const planId = text(preview.planId, 128);
|
||||
const expiresAt = text(preview.expiresAt, 64);
|
||||
const requestedSource = text(preview.requestedSource, 2_048);
|
||||
const resolvedSource = text(preview.resolvedSource, 4_096);
|
||||
const packageId = text(preview.packageId, 128);
|
||||
const displayName = text(preview.displayName, 240);
|
||||
const resolvedVersion = text(preview.resolvedVersion, 128);
|
||||
const kind = devicePackageKind(preview.kind);
|
||||
const skillEntries = deviceSkillEntries(preview.skillEntries);
|
||||
const extensionEntries = strings(preview.extensionEntries, 100);
|
||||
const ignoredLifecycleScripts = strings(preview.ignoredLifecycleScripts, 20);
|
||||
const warnings = strings(preview.warnings, 20);
|
||||
if (!planId || !expiresAt || !requestedSource || !resolvedSource || !packageId
|
||||
|| !displayName || !resolvedVersion || !kind || !skillEntries || !extensionEntries
|
||||
|| !ignoredLifecycleScripts || !warnings) return null;
|
||||
return {
|
||||
schemaVersion: 1, planId, expiresAt, requestedSource, resolvedSource, packageId,
|
||||
displayName, resolvedVersion, kind, skillEntries, extensionEntries,
|
||||
includesExecutableCode: preview.includesExecutableCode,
|
||||
ignoredLifecycleScripts, warnings, scope: 'device-parent-workers',
|
||||
};
|
||||
}
|
||||
|
||||
function devicePackageRecord(value: unknown): DevicePackageRecordV1 | null {
|
||||
const item = record(value);
|
||||
if (!item || !exactKeys(item, [
|
||||
'schemaVersion', 'packageId', 'displayName', 'resolvedVersion', 'source', 'kind',
|
||||
'skillEntries', 'extensionEntries', 'enabled', 'confirmedExecutableCode', 'installedAt',
|
||||
]) || item.schemaVersion !== 1 || typeof item.enabled !== 'boolean'
|
||||
|| typeof item.confirmedExecutableCode !== 'boolean') return null;
|
||||
const source = record(item.source);
|
||||
const packageId = text(item.packageId, 128);
|
||||
const displayName = text(item.displayName, 240);
|
||||
const resolvedVersion = text(item.resolvedVersion, 128);
|
||||
const kind = devicePackageKind(item.kind);
|
||||
const installedAt = text(item.installedAt, 64);
|
||||
const skillEntries = deviceSkillEntries(item.skillEntries);
|
||||
const extensionEntries = strings(item.extensionEntries, 100);
|
||||
if (!source || !exactKeys(source, ['kind', 'requested', 'resolved'])
|
||||
|| !['npm', 'git', 'file'].includes(String(source.kind))
|
||||
|| !packageId || !displayName || !resolvedVersion || !kind || !installedAt
|
||||
|| !skillEntries || !extensionEntries) return null;
|
||||
const requested = text(source.requested, 2_048);
|
||||
const resolved = text(source.resolved, 4_096);
|
||||
if (!requested || !resolved) return null;
|
||||
return {
|
||||
schemaVersion: 1, packageId, displayName, resolvedVersion,
|
||||
source: { kind: source.kind as DevicePackageRecordV1['source']['kind'], requested, resolved },
|
||||
kind, skillEntries, extensionEntries, enabled: item.enabled,
|
||||
confirmedExecutableCode: item.confirmedExecutableCode, installedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function devicePackageIndex(value: unknown): DevicePackageIndexV1 | null {
|
||||
const index = record(value);
|
||||
if (!index || !exactKeys(index, ['schemaVersion', 'generation', 'packages'])
|
||||
|| index.schemaVersion !== 1 || !Number.isSafeInteger(index.generation)
|
||||
|| (index.generation as number) < 0 || !Array.isArray(index.packages)
|
||||
|| index.packages.length > 200) return null;
|
||||
const packages = index.packages.map(devicePackageRecord);
|
||||
if (packages.some((item) => item === null)) return null;
|
||||
return { schemaVersion: 1, generation: index.generation as number, packages: packages as DevicePackageRecordV1[] };
|
||||
}
|
||||
|
||||
function devicePackageDetails(value: Record<string, unknown>): DevicePackageToolDetailsV1 | null {
|
||||
if (value.schema !== 'makelore-device-package.v1' || value.success !== true
|
||||
|| !['prepare', 'commit', 'list', 'set_enabled', 'uninstall'].includes(String(value.operation))) return null;
|
||||
if (value.operation === 'prepare') {
|
||||
if (!exactKeys(value, ['schema', 'operation', 'success', 'preview'])) return null;
|
||||
const preview = devicePackagePreview(value.preview);
|
||||
return preview ? { schema: 'makelore-device-package.v1', operation: 'prepare', success: true, preview } : null;
|
||||
}
|
||||
if (!exactKeys(value, ['schema', 'operation', 'success', 'index'])) return null;
|
||||
const index = devicePackageIndex(value.index);
|
||||
return index ? {
|
||||
schema: 'makelore-device-package.v1',
|
||||
operation: value.operation as Exclude<DevicePackageToolDetailsV1['operation'], 'prepare'>,
|
||||
success: true,
|
||||
index,
|
||||
} : null;
|
||||
}
|
||||
|
||||
export function productToolDetails(value: unknown): Exclude<KnownToolDetails, { schema: 'subagent.v1' | 'write-lease.v1' }> | null {
|
||||
const details = record(value);
|
||||
if (!details) return null;
|
||||
@@ -310,6 +501,8 @@ export function productToolDetails(value: unknown): Exclude<KnownToolDetails, {
|
||||
if (details.schema === 'game-assets.v1') return gameAssetDetails(details);
|
||||
if (details.schema === 'runtime-context.v1') return runtimeContextDetails(details);
|
||||
if (details.schema === 'makelore-capability.v1') return capabilityDetails(details);
|
||||
if (details.schema === 'makelore-model-tool.v1') return modelToolDetails(details);
|
||||
if (details.schema === 'makelore-device-package.v1') return devicePackageDetails(details);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user