fix(commands): support both Harness command argument formats

Keep images for newer Harness endpoints and retry without it only when an older gateway rejects the field before dispatch. Preserve cancellation and never retry business failures.

Add regression coverage, rebuild the Host bundle, and document the compatibility fix. Follow-up to #75.
This commit is contained in:
xmanrui 2026-08-27 22:11:13 +08:00
parent b9e53bfd69
commit 4c4c748864
4 changed files with 143 additions and 14 deletions

View file

@ -12,14 +12,30 @@ export function createHarnessCommandExecutor(ctx, provided) {
if (typeof gateway.invoke !== 'function') {
throw new TypeError('dsh-im requires a callable ctx.typertGateway');
}
return (sessionId, line, options = {}) => gateway.invoke({
namespace: 'commands',
method: 'execute',
// The Harness commands execute descriptor declares agentId, line, and
// images as required JSON wire fields; omitting images makes the Typert
// gateway reject the call with arguments-invalid, so IM /compact always
// carried an empty image list.
args: { agentId: sessionId, line, images: [] },
signal: options.signal,
});
return async (sessionId, line, options = {}) => {
const request = {
namespace: 'commands',
method: 'execute',
args: { agentId: sessionId, line, images: [] },
signal: options.signal,
};
try {
return await gateway.invoke(request);
} catch (error) {
// Newer Hosts require images; older Hosts reject that field before
// invoking the command. Retry only that exact pre-dispatch failure so
// a business failure can never cause compaction to run twice.
if (error?.name !== 'TypertGatewayError'
|| error.code !== 'arguments-invalid'
|| error.endpoint !== 'commands/execute'
|| error.message !== 'typert gateway: commands/execute: args fields do not match the descriptor: unexpected "images"') {
throw error;
}
options.signal?.throwIfAborted();
return gateway.invoke({
...request,
args: { agentId: sessionId, line },
});
}
};
}