Skip to content

Commit 8ce6f4b

Browse files
fix(provider-kiro): a permission with only allow_always can be approved
When Kiro offered allow_always but no allow_once, the card held only Deny and Cancel. It now offers Always allow, which saves Kiro's consent rule, only in that case. Co-Authored-By: Claude Opus 5.5 <[email protected]>
1 parent e1a7eb5 commit 8ce6f4b

3 files changed

Lines changed: 19 additions & 15 deletions

File tree

‎apps/server/src/orchestration-v2/Adapters/KiroAdapterV2.test.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { readProviderReplayTranscript } from "@t3tools/provider-testing/replayTr
5252
import { KiroSettings } from "@t3tools/provider-kiro/settings";
5353
import {
5454
KIRO_PROVIDER,
55+
kiroApprovalOptions,
5556
kiroAutopilotValue,
5657
kiroPermissionDisposition,
5758
makeKiroAdapterV2,
@@ -1332,16 +1333,15 @@ describe("Kiro permission policy", () => {
13321333
"allow",
13331334
);
13341335
// With no one-time choice, approving would save a rule beyond the thread,
1335-
// so the request goes to the user instead.
1336-
assert.equal(
1337-
kiroPermissionDisposition(
1338-
fullAccess,
1339-
writeRequest([
1340-
{ optionId: "always-accept", name: "Always allow", kind: "allow_always" },
1341-
{ optionId: "reject", name: "Deny", kind: "reject_once" },
1342-
]),
1343-
),
1344-
"ask",
1336+
// so the request goes to the user, whose card can still allow it.
1337+
const alwaysOnly = writeRequest([
1338+
{ optionId: "always-accept", name: "Always allow", kind: "allow_always" },
1339+
{ optionId: "reject", name: "Deny", kind: "reject_once" },
1340+
]);
1341+
assert.equal(kiroPermissionDisposition(fullAccess, alwaysOnly), "ask");
1342+
assert.deepEqual(
1343+
kiroApprovalOptions(alwaysOnly).map((option) => option.decision),
1344+
["acceptForSession", "decline", "cancel"],
13451345
);
13461346
});
13471347
});

‎packages/provider-kiro/src/server/acpSupport.ts‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,21 @@ export function buildKiroAcpSpawnInput(
8888
}
8989

9090
/**
91-
* Approval choices a Kiro permission request can honor. V3 sends its options
92-
* as authoritative, so only the kinds it offered appear. `allow_always` in V3
93-
* persists a consent rule (workspace or wider) rather than a session grant, so
94-
* it is not offered as "this session".
91+
* Approval choices a Kiro permission request can honor; V3's options are
92+
* authoritative. Its `allow_always` saves a consent rule for the workspace or
93+
* wider, so it is offered, as "Always allow", only without a one-time choice.
9594
*/
9695
export function kiroApprovalOptions(
9796
request: EffectAcpSchema.RequestPermissionRequest,
9897
): ReadonlyArray<ProviderApprovalOption> {
9998
const has = (kind: EffectAcpSchema.PermissionOption["kind"]) =>
10099
request.options.some((option) => option.kind === kind && option.optionId.trim().length > 0);
101100
return [
102-
...(has("allow_once") ? [{ decision: "accept", label: "Allow once" } as const] : []),
101+
...(has("allow_once")
102+
? [{ decision: "accept", label: "Allow once" } as const]
103+
: has("allow_always")
104+
? [{ decision: "acceptForSession", label: "Always allow" } as const]
105+
: []),
103106
...(has("reject_once") ? [{ decision: "decline", label: "Deny" } as const] : []),
104107
{ decision: "cancel", label: "Cancel" },
105108
];

‎packages/provider-kiro/src/testing.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { KIRO_PROVIDER, makeKiroAdapterV2, type KiroAdapterV2Options } from "./server/adapter.ts";
22
export {
3+
kiroApprovalOptions,
34
kiroAutopilotValue,
45
kiroPermissionDisposition,
56
makeKiroAcpRuntime,

0 commit comments

Comments
 (0)