```typescript
export function agedServiceOrder(events: Array<Record<string, unknown>>, step: number): string[] {
    if (step <= 0 || !Number.isInteger(step)) throw new Error('Step must be a positive whole number.');
    if (events.length === 0) throw new Error('Log cannot be empty.');

    interface WaitingRoomEntry {
        who: string;
        rank: number;
        joinTick: number;
    }

    const waitingRoom: WaitingRoomEntry[] = [];
    const namesTakenOut: string[] = [];

    for (const event of events) {
        if (!event.hasOwnProperty('kind') || !event.hasOwnProperty('tick')) continue;

        const { kind, tick } = event as { kind: string; tick: number };

        if (typeof tick !== 'number' || !Number.isInteger(tick) || tick < 0) {
            throw new Error('Tick must be a non-negative whole number.');
        }

        if (tick < (events[events.length - 2] as { tick: number }).tick) {
            throw new Error('Ticks must not run backwards.');
        }

        if (kind === 'join') {
            const { who, rank } = event as { kind: 'join'; who: string; rank: number };

            if (!who || typeof who !== 'string' || typeof rank !== 'number' || !Number.isInteger(rank) || rank < 0) {
                throw new Error('Invalid join parameters.');
            }

            if (waitingRoom.some(entry => entry.who === who)) {
                throw new Error('Caller already in the waiting room.');
            }

            waitingRoom.push({ who, rank, joinTick: tick });
        } else if (kind === 'call') {
            if (waitingRoom.length === 0) {
                throw new Error('Cannot call when the waiting room is empty.');
            }

            waitingRoom.sort((a, b) => {
                const standingA = a.rank + Math.floor(tick - a.joinTick) / step;
                const standingB = b.rank + Math.floor(tick - b.joinTick) / step;

                if (standingA !== standingB) return standingB - standingA;
                if (a.joinTick !== b.joinTick) return a.joinTick - b.joinTick;
                return a.who.localeCompare(b.who);
            });

            namesTakenOut.push(waitingRoom.pop()!.who);
        }
    }

    return namesTakenOut;
}
```