fix(host): harden skill.invoke at the enforcement boundary

Review fixes: recheck isUserInvocable on the loaded definition (list and
get collect independently, so a provider change between them could swap in
a user-disabled body — the skill-tool execute template's second check);
thread the carrier signal through the lookup and refuse an abandoned
caller's turn as cancelled; fold lookup/loader failures into the
structured internal error the list face already uses; refuse cwd-less
sessions with the skill.list stance; and reject blank trailing text at the
wire schema instead of relying on client trimming.
This commit is contained in:
Yichen Jiang
2026-08-08 11:30:14 +08:00
parent 69bd00ae76
commit c4c2355b50
6 changed files with 155 additions and 30 deletions
+1 -1
View File
@@ -109,7 +109,7 @@ const UNARY_ROUTES: UnaryRoutes = {
'command.list': { schema: commandListRequestSchema, invoke: (api, r) => api.commands.list(r) },
'command.execute': { schema: commandExecuteRequestSchema, invoke: (api, r, signal) => api.commands.execute(r, signal) },
'skill.list': { schema: skillListRequestSchema, invoke: (api, r) => api.skills.list(r) },
'skill.invoke': { schema: skillInvokeRequestSchema, invoke: (api, r) => api.skills.invoke(r) },
'skill.invoke': { schema: skillInvokeRequestSchema, invoke: (api, r, signal) => api.skills.invoke(r, signal) },
'goal.create': { schema: goalCreateRequestSchema, invoke: (api, r) => api.goals.create(r) },
'goal.edit': { schema: goalEditRequestSchema, invoke: (api, r) => api.goals.edit(r) },
'goal.pause': { schema: goalPauseRequestSchema, invoke: (api, r) => api.goals.pause(r) },