Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -182,17 +182,12 @@ describe("ACP registry replay fixtures", () => {
const errors = projection.turnItems.filter((item) => item.type === "error");
assert.lengthOf(errors, 1);
assert.equal(errors[0]?.failure.class, "provider_error");
if (testCase.code === -32602) {
assert.equal(
errors[0]?.failure.message,
'The agent did not accept the model "replay-model-b". Choose another model for this provider, then retry.',
);
} else {
assert.match(
errors[0]?.failure.message ?? "",
/^Failed to open acpRegistry provider session \S+\.$/u,
);
}
assert.equal(
errors[0]?.failure.message,
testCase.code === -32602
? 'The agent did not accept the model "replay-model-b". Choose another model for this provider, then retry.'
: "The provider needs you to sign in. Sign in from its provider settings or with its own CLI or app, then retry the turn.",
);
}).pipe(
Effect.provide(
layerProviderReplay(scenario, AcpRegistryOrchestratorReplayHarness, {
Expand Down
23 changes: 23 additions & 0 deletions packages/provider-core/src/server/failure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,29 @@ it("names how the provider process exited and the stage it failed in, without it
);
});

it("tells the user to sign in when an ACP agent rejects the session as unauthenticated", () => {
const providerSessionId = ProviderSessionId.make("provider-session:private-id");
const sessionOpenFailure = {
_tag: "ProviderSessionOpenError",
providerSessionId,
cause: new ProviderAdapter.ProviderAdapterOpenSessionError({
driver: ProviderDriverKind.make("acpRegistry"),
providerSessionId,
cause: Cause.fail({
_tag: "AcpRequestError",
code: -32000,
errorMessage: "Authentication required",
}),
}),
};

const known = knownProviderFailureMessage(sessionOpenFailure);
assert.isDefined(known, "an ACP auth-required failure should have a curated message");
assert.match(known ?? "", /sign(ed)?[ -]?in|log(ged)?[ -]?in/iu);
assert.notInclude(known ?? "", providerSessionId);
assert.equal(makeProviderFailure({ cause: Cause.fail(sessionOpenFailure) }).message, known);
});

it("keeps unrecognized reasons and provider text out of category messages", () => {
for (const cause of [
"Private launch command",
Expand Down
8 changes: 8 additions & 0 deletions packages/provider-core/src/server/failure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ function curatedCauseMessages(cause: unknown): {
category: categoryMessage(),
};
}
// ACP reserves -32000 for auth_required; the agent's own text is not shown.
case "AcpRequestError":
if ((cause as Record<string, unknown>).code !== -32000) break;
return {
known:
"The provider needs you to sign in. Sign in from its provider settings or with its own CLI or app, then retry the turn.",
category: categoryMessage(),
};
case "ContextHandoffDeliveryUncertainError":
return {
known:
Expand Down
Loading