|
1 | 1 | import { AppError } from '@agent-device/kernel/errors'; |
2 | 2 | import { normalizeTenantId, resolveSessionIsolationMode } from './config.ts'; |
3 | 3 | import { isTenantOwnedSessionName, tenantScopedSessionName } from './session-tenant-scope.ts'; |
4 | | -import { isLeaseAdmissionExempt } from './daemon-command-registry.ts'; |
| 4 | +import { |
| 5 | + isLeaseAdmissionExempt, |
| 6 | + isSessionlessPlainCloseAdmissionExempt, |
| 7 | +} from './daemon-command-registry.ts'; |
5 | 8 | import { |
6 | 9 | DEFAULT_PROXY_LEASE_TTL_MS, |
7 | 10 | findMissingProxyLeaseFields, |
@@ -69,22 +72,22 @@ export function assertRequestLeaseAdmission( |
69 | 72 | const requestLeaseScope = resolveLeaseScope(req); |
70 | 73 | assertProxyOpenLeaseMetadata(req, requestLeaseScope); |
71 | 74 | const sessionLease = session?.lease; |
72 | | - // #2016: plain `close` (no app target) on a tenant-isolated connection |
73 | | - // that never reached `open` has no daemon session and no lease to admit |
74 | | - // or release. Falling through would make the generic tenant/run/lease |
75 | | - // check below throw "tenant isolation requires lease id.", which reads as |
76 | | - // an access-control failure instead of "nothing to close". Let the close |
77 | | - // handler's own session lookup return its SESSION_NOT_FOUND response |
78 | | - // instead. Requires `session === undefined`, not just a lease-less |
79 | | - // session: a *stored* session under tenant isolation is keyed by tenant, |
80 | | - // not by run, so a lease-less stored session could belong to another run |
81 | | - // in the same tenant — admission must still verify a matching lease |
82 | | - // before that run's session can be torn down. |
| 75 | + // #2016: a tenant-isolated connection that never reached `open` has no |
| 76 | + // daemon session and no lease to admit or release. Falling through would |
| 77 | + // make the generic tenant/run/lease check below throw "tenant isolation |
| 78 | + // requires lease id.", which reads as an access-control failure instead of |
| 79 | + // "nothing to close". Let the close handler's own session lookup return |
| 80 | + // its SESSION_NOT_FOUND response instead. Requires `session === undefined`, |
| 81 | + // not just a lease-less session: a *stored* session under tenant isolation |
| 82 | + // is keyed by tenant, not by run, so a lease-less stored session could |
| 83 | + // belong to another run in the same tenant — admission must still verify a |
| 84 | + // matching lease before that run's session can be torn down. Which request |
| 85 | + // shape qualifies (plain `close`, not an app-target `close <app>`) is the |
| 86 | + // registry's call, not this module's — see `sessionlessPlainCloseAdmissionExempt`. |
83 | 87 | if ( |
84 | 88 | session === undefined && |
85 | 89 | !requestLeaseScope.leaseId && |
86 | | - req.command === 'close' && |
87 | | - (req.positionals?.length ?? 0) === 0 |
| 90 | + isSessionlessPlainCloseAdmissionExempt(req) |
88 | 91 | ) { |
89 | 92 | return undefined; |
90 | 93 | } |
|
0 commit comments